Mercurial > hg > xemacs-beta
diff src/md5.c @ 276:6330739388db r21-0b36
Import from CVS: tag r21-0b36
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:30:37 +0200 |
parents | 727739f917cb |
children | 7df0dd720c89 |
line wrap: on
line diff
--- a/src/md5.c Mon Aug 13 10:29:43 2007 +0200 +++ b/src/md5.c Mon Aug 13 10:30:37 2007 +0200 @@ -1,528 +1,621 @@ -/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm - */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. + 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. See the + GNU General Public License for more details. -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -These notices must be retained in any copies of any part of this -documentation and/or software. - */ +/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */ + +/* XEmacs frontend written by Ben Wing, Jareth Hein and Hrvoje Niksic. */ -/* Synched up with: Not in FSF. */ -/* This file has been Mule-ized. */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif -#include <config.h> -#include "lisp.h" +#include <sys/types.h> -#include "buffer.h" -#include "insdel.h" -#include "lstream.h" -#ifdef FILE_CODING -#include "file-coding.h" +#include <stdlib.h> +#include <string.h> + +#include <stdio.h> + +#if defined HAVE_LIMITS_H || _LIBC +# include <limits.h> #endif -typedef unsigned char *POINTER;/* POINTER defines a generic pointer type */ -typedef unsigned short int UINT2;/* UINT2 defines a two byte word */ -typedef unsigned int UINT4;/* UINT4 defines a four byte word */ - -#define PROTO_LIST(list) list -#define MD_CTX MD5_CTX -#define MDInit MD5Init -#define MDUpdate MD5Update -#define MDFinal MD5Final +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + doing that would require that the configure script compile and *run* + the resulting executable. Locally running cross-compiled executables + is usually not possible. */ -/* MD5 context. */ -typedef struct { - UINT4 state[4]; /* state (ABCD) */ - UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ -} MD5_CTX; +#ifdef _LIBC +# include <sys/types.h> +typedef u_int32_t md5_uint32; +#else +# if defined __STDC__ && __STDC__ +# define UINT_MAX_32_BITS 4294967295U +# else +# define UINT_MAX_32_BITS 0xFFFFFFFF +# endif -void MD5Init PROTO_LIST ((MD5_CTX *)); -void MD5Update PROTO_LIST - ((MD5_CTX *, CONST unsigned char *, unsigned int)); -void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); +/* If UINT_MAX isn't defined, assume it's a 32-bit type. + This should be valid for all systems GNU cares about because + that doesn't include 16-bit systems, and only modern systems + (that certainly have <limits.h>) have 64+-bit integral types. */ + +# ifndef UINT_MAX +# define UINT_MAX UINT_MAX_32_BITS +# endif -/* Constants for MD5Transform routine. - */ -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 +# if UINT_MAX == UINT_MAX_32_BITS + typedef unsigned int md5_uint32; +# else +# if USHRT_MAX == UINT_MAX_32_BITS + typedef unsigned short md5_uint32; +# else +# if ULONG_MAX == UINT_MAX_32_BITS + typedef unsigned long md5_uint32; +# else + /* The following line is intended to evoke an error. + Using #error is not portable enough. */ + "Cannot determine unsigned 32-bit data type." +# endif +# endif +# endif +#endif -static void MD5Transform PROTO_LIST ((UINT4 [4], CONST unsigned char [64])); -static void Encode PROTO_LIST - ((unsigned char *, UINT4 *, unsigned int)); -static void Decode PROTO_LIST - ((UINT4 *, CONST unsigned char *, unsigned int)); -static void MD5_memcpy PROTO_LIST ((POINTER, CONST POINTER, unsigned int)); -static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); +#include "lisp.h" +#include "buffer.h" +#include "lstream.h" +#ifdef FILE_CODING +# include "file-coding.h" +#endif -static unsigned char PADDING[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128]; }; -/* F, G, H and I are basic MD5 functions. - */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define I(x, y, z) ((y) ^ ((x) | (~z))) +#ifdef WORDS_BIGENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif -/* ROTATE_LEFT rotates x left n bits. - */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. -Rotation is separate from addition to prevent recomputation. - */ -#define FF(a, b, c, d, x, s, ac) { \ - (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define GG(a, b, c, d, x, s, ac) { \ - (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define HH(a, b, c, d, x, s, ac) { \ - (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define II(a, b, c, d, x, s, ac) { \ - (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } +static void md5_process_block (CONST void *, size_t, struct md5_ctx *); + -/* MD5 initialization. Begins an MD5 operation, writing a new context. - */ -void -MD5Init (MD5_CTX *context) +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +static void +md5_init_ctx (struct md5_ctx *ctx) { - context->count[0] = context->count[1] = 0; + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; - /* Load magic initialization constants. */ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; } -/* MD5 block update operation. Continues an MD5 message-digest - operation, processing another message block, and updating the - context. - */ -void -MD5Update (MD5_CTX *context, CONST unsigned char *input, unsigned int inputLen) -{ - unsigned int i, indice, partLen; - - /* Compute number of bytes mod 64 */ - indice = (unsigned int)((context->count[0] >> 3) & 0x3F); - - /* Update number of bits */ - if ((context->count[0] += ((UINT4)inputLen << 3)) - < ((UINT4)inputLen << 3)) - context->count[1]++; - context->count[1] += ((UINT4)inputLen >> 29); +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. - partLen = 64 - indice; - - /* Transform as many times as possible. */ - if (inputLen >= partLen) - { - MD5_memcpy ((POINTER)&context->buffer[indice], (CONST POINTER)input, - partLen); - MD5Transform (context->state, context->buffer); + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +static void * +md5_read_ctx (CONST struct md5_ctx *ctx, void *resbuf) +{ + ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); - for (i = partLen; i + 63 < inputLen; i += 64) - MD5Transform (context->state, &input[i]); - - indice = 0; - } - else - i = 0; - - /* Buffer remaining input */ - MD5_memcpy ((POINTER)&context->buffer[indice], (CONST POINTER)&input[i], - inputLen-i); + return resbuf; } -/* MD5 finalization. Ends an MD5 message-digest operation, writing the - message digest and zeroizing the context. */ -void -MD5Final (unsigned char digest[16], MD5_CTX *context) +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +static void * +md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) { - unsigned char bits[8]; - unsigned int indice, padLen; - - /* Save number of bits */ - Encode (bits, context->count, 8); + /* Take yet unprocessed bytes into account. */ + md5_uint32 bytes = ctx->buflen; + size_t pad; - /* Pad out to 56 mod 64. -*/ - indice = (unsigned int)((context->count[0] >> 3) & 0x3f); - padLen = (indice < 56) ? (56 - indice) : (120 - indice); - MD5Update (context, PADDING, padLen); + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); - /* Append length (before padding) */ - MD5Update (context, bits, 8); - /* Store state in digest */ - Encode (digest, context->state, 16); + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); - /* Zeroize sensitive information. -*/ - MD5_memset ((POINTER)context, 0, sizeof (*context)); + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); } -/* MD5 basic transformation. Transforms state based on block. - */ -static void -MD5Transform (UINT4 state[4], CONST unsigned char block[64]) +#ifndef emacs /* unused in Emacs */ +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +md5_stream (FILE *stream, void *resblock) { - UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode (x, block, 64); + /* Important: BLOCKSIZE must be a multiple of 64. */ +#define BLOCKSIZE 4096 + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + /* Initialize the computation context. */ + md5_init_ctx (&ctx); - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + /* Read block. Take care for partial reads. */ + do + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + } + while (sum < BLOCKSIZE && n != 0); + if (n == 0 && ferror (stream)) + return 1; - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + /* If end of file is reached, end the loop. */ + if (n == 0) + break; - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block (buffer, BLOCKSIZE, &ctx); + } - /* Zeroize sensitive information. -*/ - MD5_memset ((POINTER)x, 0, sizeof (x)); + /* Add the last bytes if necessary. */ + if (sum > 0) + md5_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx (&ctx, resblock); + return 0; } -/* Encodes input (UINT4) into output (unsigned char). Assumes len is - a multiple of 4. - */ +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (const char *buffer, size_t len, void *resblock) +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx (&ctx, resblock); +} +#endif /* not emacs */ + + static void -Encode (unsigned char *output, UINT4 *input, unsigned int len) +md5_process_bytes (CONST void *buffer, size_t len, struct md5_ctx *ctx) { - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) { - output[j] = (unsigned char)(input[i] & 0xff); - output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); - output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); - output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (left_over + add > 64) + { + md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx); + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + (left_over + add) & 63); + ctx->buflen = (left_over + add) & 63; + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len > 64) + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + memcpy (ctx->buffer, buffer, len); + ctx->buflen = len; } } -/* Decodes input (unsigned char) into output (UINT4). Assumes len is - a multiple of 4. - */ -static void -Decode (UINT4 *output, CONST unsigned char *input, unsigned int len) -{ - unsigned int i, j; - for (i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | - (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); -} +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) -static void -MD5_memcpy (POINTER output, CONST POINTER input, unsigned int len) -{ - memcpy (output, input, len); -} +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ static void -MD5_memset (POINTER output, int value, unsigned int len) -{ - memset (output, value, len); -} - -/* unused */ -#if 0 -static void -LispMDString (char *string) +md5_process_block (CONST void *buffer, size_t len, struct md5_ctx *ctx) { - MD_CTX context; - unsigned char digest[16]; - unsigned int len = strlen(string); + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); - MDInit (&context); - MDUpdate (&context, string, len); - MDFinal (digest, &context); + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; } -#endif -/* XEmacs interface code. */ -Lisp_Object Qmd5; - -DEFUN ("md5", Fmd5, 1, 5, 0, /* -Return the MD5 (a secure message digest algorithm) of an object. -OBJECT is either a string or a buffer. -Optional arguments START and END denote buffer positions for computing the -hash of a portion of OBJECT. The optional CODING argument specifies the coding -system the text is to be represented in while computing the digest. This only -has meaning with MULE, and defaults to the current format of the data. -If ERROR-ME-NOT is nil, report an error if the coding system can't be -determined. Else assume binary coding if all else fails. -*/ - (object, start, end, coding, error_me_not)) +#ifdef emacs +#ifdef FILE_CODING +/* Find out what format the buffer will be saved in, so we can make + the digest based on what it will look like on disk. */ +static Lisp_Object +md5_coding_system (Lisp_Object object, Lisp_Object coding, Lisp_Object istream, + int error_me_not) { - /* This function can GC */ - MD_CTX context; - unsigned char digest[16]; - unsigned char thehash[32]; - int i; - - MDInit (&context); + Lisp_Object coding_system; - if (NILP (object)) - { - MDUpdate (&context, (CONST unsigned char *) "", 0); - } - else + if (NILP (coding)) { - Lisp_Object instream, outstream; - Lstream *istr, *ostr; - static Extbyte_dynarr *conversion_out_dynarr; - char tempbuf[1024]; /* some random amount */ - struct gcpro gcpro1, gcpro2; -#ifdef FILE_CODING - Lisp_Object conv_out_stream, coding_system; - Lstream *costr; - struct gcpro gcpro3; -#endif - - if (!conversion_out_dynarr) - conversion_out_dynarr = Dynarr_new (Extbyte); - else - Dynarr_reset (conversion_out_dynarr); - - /* set up the in stream */ if (BUFFERP (object)) { - struct buffer *b = decode_buffer (object, 1); - Bufpos begv, endv; - /* Figure out where we need to get info from */ - get_buffer_range_char (b, start, end, &begv, &endv, GB_ALLOW_NIL); - - instream = make_lisp_buffer_input_stream (b, begv, endv, 0); + /* Use the file coding for this buffer by default. */ + coding_system = XBUFFER (object)->buffer_file_coding_system; } else { - Bytecount bstart, bend; - CHECK_STRING (object); - get_string_range_byte (object, start, end, &bstart, &bend, - GB_HISTORICAL_STRING_BEHAVIOR); - instream = make_lisp_string_input_stream (object, bstart, bend); + /* Attempt to autodetect the coding of the string. This is + VERY hit-and-miss. */ + enum eol_type eol = EOL_AUTODETECT; + coding_system = Fget_coding_system (Qundecided); + determine_real_coding_system (XLSTREAM (istream), + &coding_system, &eol); } - istr = XLSTREAM (instream); - -#ifdef FILE_CODING - /* Find out what format the buffer will be saved in, so we can make - the digest based on what it will look like on disk */ - if (NILP(coding)) - { - if (BUFFERP(object)) - { - /* Use the file coding for this buffer by default */ - coding_system = XBUFFER(object)->buffer_file_coding_system; - } - else - { - /* attempt to autodetect the coding of the string. Note: this VERY hit-and-miss */ - enum eol_type eol = EOL_AUTODETECT; - coding_system = Fget_coding_system(Qundecided); - determine_real_coding_system(istr, &coding_system, &eol); - } - if (NILP(coding_system)) - coding_system = Fget_coding_system(Qbinary); - else - { - coding_system = Ffind_coding_system (coding_system); - if (NILP(coding_system)) - coding_system = Fget_coding_system(Qbinary); - } - } + if (NILP (coding_system)) + coding_system = Fget_coding_system (Qbinary); else { - coding_system = Ffind_coding_system (coding); - if (NILP(coding_system)) - { - if (NILP(error_me_not)) - signal_simple_error("No such coding system", coding); - else - coding_system = Fget_coding_system(Qbinary); /* default to binary */ - } + coding_system = Ffind_coding_system (coding_system); + if (NILP (coding_system)) + coding_system = Fget_coding_system (Qbinary); + } + } + else + { + coding_system = Ffind_coding_system (coding); + if (NILP (coding_system)) + { + if (error_me_not) + /* Default to binary. */ + coding_system = Fget_coding_system (Qbinary); + else + signal_simple_error ("No such coding system", coding); } + } + return coding_system; +} +#endif /* FILE_CODING */ + + +DEFUN ("md5", Fmd5, 1, 5, 0, /* +Return the MD5 message digest of OBJECT, a buffer or string. + +Optional arguments START and END denote positions for computing the +digest of a portion of OBJECT. + +The optional CODING argument specifies the coding system the text is to be +represented in while computing the digest. If unspecified, it defaults +to the current format of the data, or is guessed. + +If NOERROR is non-nil, silently assume binary coding if the guesswork +fails. Normally, an error is signaled in such case. + +CODING and NOERROR arguments are meaningful only in XEmacsen with +file-coding or Mule support. Otherwise, they are ignored. +*/ + (object, start, end, coding, noerror)) +{ + /* This function can GC */ + /* Can this really GC? How? */ + struct md5_ctx ctx; + unsigned char digest[16]; + unsigned char thehash[33]; + int i; + + Lisp_Object instream; + struct gcpro gcpro1; +#ifdef FILE_CODING + Lisp_Object raw_instream; + struct gcpro ngcpro1; #endif - /* setup the out stream */ - outstream = make_dynarr_output_stream((unsigned_char_dynarr *)conversion_out_dynarr); - ostr = XLSTREAM (outstream); + /* Set up the input stream. */ + if (BUFFERP (object)) + { + struct buffer *b; + Bufpos begv, endv; + CHECK_LIVE_BUFFER (object); + b = XBUFFER (object); + /* Figure out where we need to get info from */ + get_buffer_range_char (b, start, end, &begv, &endv, GB_ALLOW_NIL); + + instream = make_lisp_buffer_input_stream (b, begv, endv, 0); + } + else + { + Bytecount bstart, bend; + CHECK_STRING (object); + get_string_range_byte (object, start, end, &bstart, &bend, + GB_HISTORICAL_STRING_BEHAVIOR); + instream = make_lisp_string_input_stream (object, bstart, bend); + } + GCPRO1 (instream); + #ifdef FILE_CODING - /* setup the conversion stream */ - conv_out_stream = make_encoding_output_stream (ostr, coding_system); - costr = XLSTREAM (conv_out_stream); - GCPRO3 (instream, outstream, conv_out_stream); -#else - GCPRO2 (instream, outstream); + /* Determine the coding and set up the conversion stream. */ + coding = md5_coding_system (object, coding, instream, !NILP (noerror)); + raw_instream = instream; + instream = make_encoding_input_stream (XLSTREAM (instream), coding); + NGCPRO1 (raw_instream); #endif - /* Get the data while doing the conversion */ - while (1) { - int size_in_bytes = Lstream_read (istr, tempbuf, sizeof (tempbuf)); - if (!size_in_bytes) - break; - /* It does seem the flushes are necessary... */ -#ifdef FILE_CODING - Lstream_write (costr, tempbuf, size_in_bytes); - Lstream_flush (costr); -#else - Lstream_write (ostr, tempbuf, size_in_bytes); -#endif - Lstream_flush (ostr); + /* Initialize MD5 context. */ + md5_init_ctx (&ctx); + + /* Get the data while doing the conversion. */ + while (1) + { + Bufbyte tempbuf[1024]; /* some random amount */ + int size_in_bytes = Lstream_read (XLSTREAM (instream), + tempbuf, sizeof (tempbuf)); + if (!size_in_bytes) + break; - /* Update the digest */ - - MDUpdate (&context, (unsigned char *)Dynarr_atp(conversion_out_dynarr, 0), - Dynarr_length(conversion_out_dynarr)); - /* reset the dynarr */ - Lstream_rewind(ostr); - } - Lstream_close (istr); + /* Process the bytes. */ + md5_process_bytes (tempbuf, size_in_bytes, &ctx); + } + Lstream_delete (XLSTREAM (instream)); #ifdef FILE_CODING - Lstream_close (costr); + Lstream_delete (XLSTREAM (raw_instream)); + NUNGCPRO; #endif - Lstream_close (ostr); + UNGCPRO; - UNGCPRO; - Lstream_delete (istr); - Lstream_delete (ostr); -#ifdef FILE_CODING - Lstream_delete (costr); -#endif - } - - MDFinal (digest, &context); + md5_finish_ctx (&ctx, digest); for (i = 0; i < 16; i++) sprintf ((char *) (thehash + (i * 2)), "%02x", digest[i]); - return (make_string (thehash, 32)); + return make_string (thehash, 32); } void syms_of_md5 (void) { DEFSUBR (Fmd5); - defsymbol (&Qmd5, "md5"); } void vars_of_md5 (void) { - Fprovide (Qmd5); + Fprovide (intern ("md5")); } +#endif /* emacs */