dbsql/test/scr050/tcl_md5.c
Gregory Burd fb683f6a7e Whitespace, cleanup, and work to get the test harness functional
again.  It compiles, but still needs to be debugged.  Then, it's
time to run the tests and fix everything they find (a lot I
expect).
2009-09-03 17:31:03 -04:00

423 lines
11 KiB
C

/*
* DBSQL uses this code for testing only. It is not a part of
* the DBSQL library. This file implements two new TCL commands
* "md5" and "md5file" that compute md5 checksums on arbitrary text
* and on complete files. These commands are used by the "testfixture"
* program to help verify the correct operation of the DBSQL library.
*
* The original use of these TCL commands was to test the ROLLBACK
* feature of DBSQL. First compute the MD5-checksum of the database.
* Then make some changes but rollback the changes rather than commit
* them. Compute a second MD5-checksum of the file and verify that the
* two checksums are the same. Such is the original use of this code.
* New uses may have been added since this comment was written.
*/
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
#ifndef NO_SYSTEM_INCLUDES
#include <ctype.h>
#endif
#include <tcl.h>
#include <stdlib.h>
#include <string.h>
#include <dbsql.h>
/* The four core functions - F1 is optimized somewhat */
#define F1(x, y, z) (z ^ (x & (y ^ z))) /* F1(x, y, z) (x & y | ~x & z) */
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
struct Context {
u_int32_t buf[4];
u_int32_t bits[2];
unsigned char in[64];
};
typedef char MD5Context[88];
/*
* Note: this code is harmless on little-endian machines.
*/
static void
byteReverse(buf, longs)
unsigned char *buf;
unsigned longs;
{
u_int32_t t;
do {
t = (u_int32_t)((unsigned)buf[3] << 8 | buf[2]) << 16 |
((unsigned)buf[1] << 8 | buf[0]);
*(u_int32_t *)buf = t;
buf += 4;
} while (--longs);
}
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
static void
MD5Transform(buf, in)
u_int32_t buf[4];
const u_int32_t in[16];
{
register u_int32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
static void
MD5Init(pCtx)
MD5Context *pCtx;
{
struct Context *ctx = (struct Context *)pCtx;
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
static void
MD5Update(pCtx, buf, len)
MD5Context *pCtx;
const unsigned char *buf;
unsigned int len;
{
struct Context *ctx = (struct Context *)pCtx;
u_int32_t t;
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((u_int32_t)len << 3)) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if ( t ) {
unsigned char *p = (unsigned char *)ctx->in + t;
t = 64-t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (u_int32_t *)ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (u_int32_t *)ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
static void
MD5Final(digest, pCtx)
unsigned char digest[16];
MD5Context *pCtx;
{
struct Context *ctx = (struct Context *)pCtx;
unsigned count;
unsigned char *p;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (u_int32_t *)ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count-8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
((u_int32_t *)ctx->in)[ 14 ] = ctx->bits[0];
((u_int32_t *)ctx->in)[ 15 ] = ctx->bits[1];
MD5Transform(ctx->buf, (u_int32_t *)ctx->in);
byteReverse((unsigned char *)ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
}
/*
* Convert a digest into base-16. digest should be declared as
* "unsigned char digest[16]" in the calling function. The MD5
* digest is stored in the first 16 bytes. zBuf should
* be "char zBuf[33]".
*/
static void
DigestToBase16(digest, zBuf)
unsigned char *digest;
char *zBuf;
{
static char const zEncode[] = "0123456789abcdef";
int i, j;
for(j=i=0; i<16; i++){
int a = digest[i];
zBuf[j++] = zEncode[(a>>4)&0xf];
zBuf[j++] = zEncode[a & 0xf];
}
zBuf[j] = 0;
}
/*
* A TCL command for md5. The argument is the text to be hashed. The
* Result is the hash in base64.
*/
static int
md5_cmd(cd, interp, argc, argv)
void *cd;
Tcl_Interp *interp;
int argc;
const char **argv;
{
MD5Context ctx;
unsigned char digest[16];
if( argc!=2 ){
Tcl_AppendResult(interp,"wrong # args: should be \"",
argv[0], " TEXT\"", 0);
return TCL_ERROR;
}
MD5Init(&ctx);
MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1]));
MD5Final(digest, &ctx);
DigestToBase16(digest, interp->result);
return TCL_OK;
}
/*
* A TCL command to take the md5 hash of a file. The argument is the
* name of the file.
*/
static int
md5file_cmd(cd, interp, argc, argv)
void *cd;
Tcl_Interp *interp;
int argc;
const char **argv;
{
FILE *in;
MD5Context ctx;
unsigned char digest[16];
char zBuf[10240];
if( argc!=2 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" FILENAME\"", 0);
return TCL_ERROR;
}
in = fopen(argv[1],"rb");
if( in==0 ){
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
"\" for reading", 0);
return TCL_ERROR;
}
MD5Init(&ctx);
for(;;){
int n;
n = fread(zBuf, 1, sizeof(zBuf), in);
if( n<=0 ) break;
MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
}
fclose(in);
MD5Final(digest, &ctx);
DigestToBase16(digest, interp->result);
return TCL_OK;
}
/*
* Register the two TCL commands above with the TCL interpreter.
*/
int
__testset_MD5_init(interp)
Tcl_Interp *interp;
{
Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, 0, 0);
Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, 0, 0);
return TCL_OK;
}
/*
* During testing, the special md5sum() aggregate function is available.
* inside DBSQL. The following routines implement that function.
*/
void
__tcl_sql_func_md5step(context, argc, argv)
dbsql_func_t *context;
int argc;
const char **argv;
{
MD5Context *p;
int i;
if( argc<1 ) return;
p = dbsql_aggregate_context(context, sizeof(*p));
if( p==0 ) return;
if( dbsql_aggregate_count(context)==1 ){
MD5Init(p);
}
for(i=0; i<argc; i++){
if( argv[i] ){
MD5Update(p, (unsigned char*)argv[i], strlen(argv[i]));
}
}
}
void
__tcl_sql_func_md5finalize(context)
dbsql_func_t *context;
{
MD5Context *p;
unsigned char digest[16];
char zBuf[33];
p = dbsql_aggregate_context(context, sizeof(*p));
MD5Final(digest,p);
DigestToBase16(digest, zBuf);
dbsql_set_result_string(context, zBuf, strlen(zBuf));
}