/*---------------------------------------------------------------------------- File name : fitsmd5.c Author : N. Devillard Created on : May 2001 Description : Display/Add/Update the DATAMD5 keyword/value This is a stand-alone utility. Compile it with any ANSI C compiler: % cc -o fitsmd5 fitsmd5.c [optional optimization options] ---------------------------------------------------------------------------*/ /* $Id: fitsmd5.c,v 1.2 2002/02/25 12:54:39 ndevilla Exp $ $Author: ndevilla $ $Date: 2002/02/25 12:54:39 $ $Revision: 1.2 $ */ /*---------------------------------------------------------------------------- Includes ---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include /*---------------------------------------------------------------------------- Defines ---------------------------------------------------------------------------*/ /* Definitions related to FITS */ #define FITSLINESZ 80 /* a FITS line is 80 chars */ #define FITSCARDS 36 /* 36 cards per block */ #define FITSBLOCKSZ (FITSLINESZ*FITSCARDS) /* FITS block size=2880 */ /* Definitions related to MD5 */ #define MD5HASHSZ 32 /* an MD5 key length is 32 bytes = 128 bits */ /* FITS keyword used to store MD5 key */ #define FITSMD5KEY "DATAMD5 " /*---------------------------------------------------------------------------- New types ---------------------------------------------------------------------------*/ /* The following types defined for MD5 computation only */ typedef unsigned int word32 ; struct MD5Context { word32 buf[4]; word32 bits[2]; unsigned char in[64]; }; /*---------------------------------------------------------------------------- Private function prototypes ---------------------------------------------------------------------------*/ static void MD5Init(struct MD5Context *); static void MD5Update(struct MD5Context *, unsigned char *, unsigned); static void MD5Final(unsigned char *, struct MD5Context *); static void MD5Transform(word32 *, word32 *); static void byteReverse(unsigned char *, unsigned); static int fits_md5_check(char *, int); static char * fits_pretty_string(char *); static char * fits_getvalue(char *); static void usage(void); /*---------------------------------------------------------------------------- Global variables ---------------------------------------------------------------------------*/ static char * pname = NULL ; static char prog_desc[] = "Compute/Update the DATAMD5 keyword/value" ; static int silent_process=0 ; /*---------------------------------------------------------------------------- MD5 function code ---------------------------------------------------------------------------*/ /* * Reverse bytes in a 32-bit word. This code is harmless on little endian * machines. */ static void byteReverse(unsigned char *buf, unsigned longs) { word32 t; do { t = (word32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsigned) buf[1] << 8 | buf[0]); *(word32 *) buf = t; buf += 4; } while (--longs); } /* * Start MD5 accumulation. Set bit count to 0 and buffer to MD5 * initialization constants. */ static void MD5Init(struct MD5Context *ctx) { 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(struct MD5Context *ctx, unsigned char *buf, unsigned len) { register word32 t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((word32) 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) { memmove(p, buf, len); return; } memmove(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (word32 *) ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { memmove(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (word32 *) ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memmove(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(unsigned char digest[16], struct MD5Context *ctx) { unsigned int 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, (word32 *) 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 */ ((word32 *) ctx->in)[14] = ctx->bits[0]; ((word32 *) ctx->in)[15] = ctx->bits[1]; MD5Transform(ctx->buf, (word32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); memmove(digest, ctx->buf, 16); memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ 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<>(32-s), w += x ) /* * 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(word32 buf[4], word32 in[16]) { register word32 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; } /*---------------------------------------------------------------------------- FITS-related functions ---------------------------------------------------------------------------*/ /* Pretty-print a FITS string value */ static char * fits_pretty_string(char * s) { static char pretty[FITSLINESZ+1] ; int i,j ; if (s==NULL) return NULL ; pretty[0] = (char)0 ; if (s[0]!='\'') return s ; /* skip first quote */ i=1 ; j=0 ; /* trim left-side blanks */ while (s[i]==' ') { if (i==(int)strlen(s)) break ; i++ ; } if (i>=(int)(strlen(s)-1)) return pretty ; /* copy string, changing double quotes to single ones */ while (i<(int)strlen(s)) { if (s[i]=='\'') { i++ ; } pretty[j]=s[i]; i++ ; j++ ; } /* NULL-terminate the pretty string */ pretty[j+1]=(char)0; /* trim right-side blanks */ j = (int)strlen(pretty)-1; while (pretty[j]==' ') j-- ; pretty[j+1]=(char)0; return pretty; } /* Get the FITS value in a FITS card */ static char * fits_getvalue(char * line) { static char value[FITSLINESZ+1] ; int i ; int from, to ; int inq ; if (line==NULL) { return NULL ; } memset(value, 0, FITSLINESZ+1); /* Get past the keyword */ i=0 ; while (line[i]!='=' && iFITSLINESZ) { return NULL ; } i++ ; while (line[i]==' ' && iFITSLINESZ) { return NULL ; } from=i; /* * Now in the value section * Look for the first slash '/' outside of a string */ inq = 0 ; while (i=0) i-- ; if (i<0) { return NULL ; } to=i ; if (to\n" "options are:\n" "\t-u update MD5 keyword in the file: %s\n" "\t-s silent mode\n" "\n" "\t-a compute MD5 sum of the complete file (incl.header)\n" "\n" "This utility computes the MD5 checksum of all data sections\n" "in a given FITS file, and compares it against the value\n" "declared in DATAMD5 if present. It can also update the value\n" "of this keyword (if present) with its own computed MD5 sum.\n" "\n" "You can also use it with the -a option to compute the MD5 sum\n" "on the complete file (all bits). In this case, the file needs\n" "not be FITS. This option is only provided to check this program\n" "against other MD5 computation tools.\n" "NB: Other options cannot be used together with -a.\n" "\n", pname, FITSMD5KEY); exit(0) ; } /*---------------------------------------------------------------------------- Main code ---------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { int i ; int update_header ; int total_md5 ; int err ; pname=argv[0]; update_header = 0 ; total_md5 = 0 ; if (argc<2) { usage(); } /* Parse arguments for options */ for (i=1 ; i0) { fprintf(stderr, "%s: %d error(s) during process\n", pname, err); } return err ; }