;;; md5.el -- MD5 Message Digest Algorithm ;;; Gareth Rees ;; LCD Archive Entry: ;; md5|Gareth Rees|gdr11@cl.cam.ac.uk| ;; MD5 cryptographic message digest algorithm| ;; 13-Nov-95|1.0|~/misc/md5.el.Z| ;;; Details: ------------------------------------------------------------------ ;; This is a direct translation into Emacs LISP of the reference C ;; implementation of the MD5 Message-Digest Algorithm written by RSA ;; Data Security, Inc. ;; ;; The algorithm takes a message (that is, a string of bytes) and ;; computes a 16-byte checksum or "digest" for the message. This digest ;; is supposed to be cryptographically strong in the sense that if you ;; are given a 16-byte digest D, then there is no easier way to ;; construct a message whose digest is D than to exhaustively search the ;; space of messages. However, the robustness of the algorithm has not ;; been proven, and a similar algorithm (MD4) was shown to be unsound, ;; so treat with caution! ;; ;; The C algorithm uses 32-bit integers; because GNU Emacs ;; implementations provide 28-bit integers (with 24-bit integers on ;; versions prior to 19.29), the code represents a 32-bit integer as the ;; cons of two 16-bit integers. The most significant word is stored in ;; the car and the least significant in the cdr. The algorithm requires ;; at least 17 bits of integer representation in order to represent the ;; carry from a 16-bit addition. ;;; Usage: -------------------------------------------------------------------- ;; To compute the MD5 Message Digest for a message M (represented as a ;; string or as a vector of bytes), call ;; ;; (md5-encode M) ;; ;; which returns the message digest as a vector of 16 bytes. If you ;; need to supply the message in pieces M1, M2, ... Mn, then call ;; ;; (md5-init) ;; (md5-update M1) ;; (md5-update M2) ;; ... ;; (md5-update Mn) ;; (md5-final) ;;; Copyright and licence: ---------------------------------------------------- ;; Copyright (C) 1995, 1996, 1997 by Gareth Rees ;; Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm ;; ;; md5.el 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. ;; ;; md5.el 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. ;; ;; The original copyright notice is given below, as required by the ;; licence for the original code. This code is distributed under *both* ;; RSA's original licence and the GNU General Public Licence. (There ;; should be no problems, as the former is more liberal than the ;; latter). ;;; Original copyright notice: ------------------------------------------------ ;; Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ;; ;; 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. ;; ;; 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. ;; ;; 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. ;; ;; These notices must be retained in any copies of any part of this ;; documentation and/or software. ;;; Code: --------------------------------------------------------------------- (defvar md5-program "md5" "*Program that reads a message on its standard input and writes an MD5 digest on its output.") (defvar md5-maximum-internal-length 4096 "*The maximum size of a piece of data that should use the MD5 routines written in lisp. If a message exceeds this, it will be run through an external filter for processing. Also see the `md5-program' variable. This variable has no effect if you call the md5-init|update|final functions - only used by the `md5' function's simpler interface.") (defvar md5-bits (make-vector 4 0) "Number of bits handled, modulo 2^64. Represented as four 16-bit numbers, least significant first.") (defvar md5-buffer (make-vector 4 '(0 . 0)) "Scratch buffer (four 32-bit integers).") (defvar md5-input (make-vector 64 0) "Input buffer (64 bytes).") (defun md5-unhex (x) (if (> x ?9) (if (>= x ?a) (+ 10 (- x ?a)) (+ 10 (- x ?A))) (- x ?0))) (defun md5-encode (message) "Encodes MESSAGE using the MD5 message digest algorithm. MESSAGE must be a string or an array of bytes. Returns a vector of 16 bytes containing the message digest." (if (or (null md5-maximum-internal-length) (<= (length message) md5-maximum-internal-length)) (progn (md5-init) (md5-update message) (md5-final)) (save-excursion (set-buffer (get-buffer-create " *md5-work*")) (erase-buffer) (insert message) (call-process-region (point-min) (point-max) md5-program t (current-buffer)) ;; MD5 digest is 32 chars long ;; mddriver adds a newline to make neaten output for tty ;; viewing, make sure we leave it behind. (let ((data (buffer-substring (point-min) (+ (point-min) 32))) (vec (make-vector 16 0)) (ctr 0)) (while (< ctr 16) (aset vec ctr (+ (* 16 (md5-unhex (aref data (* ctr 2)))) (md5-unhex (aref data (1+ (* ctr 2)))))) (setq ctr (1+ ctr))))))) (defsubst md5-add (x y) "Return 32-bit sum of 32-bit integers X and Y." (let ((m (+ (car x) (car y))) (l (+ (cdr x) (cdr y)))) (cons (logand 65535 (+ m (lsh l -16))) (logand l 65535)))) ;; FF, GG, HH and II are basic MD5 functions, providing transformations ;; for rounds 1, 2, 3 and 4 respectively. Each function follows this ;; pattern of computation (where ROTATE(x,y) means rotate 32-bit value x ;; by y bits to the left): ;; ;; FF(a,b,c,d,x,s,ac) = ROTATE(a + F(b,c,d) + x + ac,s) + b ;; ;; so we use the macro `md5-make-step' to construct each one. The ;; helper functions F, G, H and I operate on 16-bit numbers; the full ;; operation splits its inputs, operates on the halves separately and ;; then puts the results together. (defsubst md5-F (x y z) (logior (logand x y) (logand (lognot x) z))) (defsubst md5-G (x y z) (logior (logand x z) (logand y (lognot z)))) (defsubst md5-H (x y z) (logxor x y z)) (defsubst md5-I (x y z) (logxor y (logior x (logand 65535 (lognot z))))) (defmacro md5-make-step (name func) (` (defun (, name) (a b c d x s ac) (let* ((m1 (+ (car a) ((, func) (car b) (car c) (car d)) (car x) (car ac))) (l1 (+ (cdr a) ((, func) (cdr b) (cdr c) (cdr d)) (cdr x) (cdr ac))) (m2 (logand 65535 (+ m1 (lsh l1 -16)))) (l2 (logand 65535 l1)) (m3 (logand 65535 (if (> s 15) (+ (lsh m2 (- s 32)) (lsh l2 (- s 16))) (+ (lsh m2 s) (lsh l2 (- s 16)))))) (l3 (logand 65535 (if (> s 15) (+ (lsh l2 (- s 32)) (lsh m2 (- s 16))) (+ (lsh l2 s) (lsh m2 (- s 16))))))) (md5-add (cons m3 l3) b))))) (md5-make-step md5-FF md5-F) (md5-make-step md5-GG md5-G) (md5-make-step md5-HH md5-H) (md5-make-step md5-II md5-I) (defun md5-init () "Initialise the state of the message-digest routines." (aset md5-bits 0 0) (aset md5-bits 1 0) (aset md5-bits 2 0) (aset md5-bits 3 0) (aset md5-buffer 0 '(26437 . 8961)) (aset md5-buffer 1 '(61389 . 43913)) (aset md5-buffer 2 '(39098 . 56574)) (aset md5-buffer 3 '( 4146 . 21622))) (defun md5-update (string) "Update the current MD5 state with STRING (an array of bytes)." (let ((len (length string)) (i 0) (j 0)) (while (< i len) ;; Compute number of bytes modulo 64 (setq j (% (/ (aref md5-bits 0) 8) 64)) ;; Store this byte (truncating to 8 bits to be sure) (aset md5-input j (logand 255 (aref string i))) ;; Update number of bits by 8 (modulo 2^64) (let ((c 8) (k 0)) (while (and (> c 0) (< k 4)) (let ((b (aref md5-bits k))) (aset md5-bits k (logand 65535 (+ b c))) (setq c (if (> b (- 65535 c)) 1 0) k (1+ k))))) ;; Increment number of bytes processed (setq i (1+ i)) ;; When 64 bytes accumulated, pack them into sixteen 32-bit ;; integers in the array `in' and then tranform them. (if (= j 63) (let ((in (make-vector 16 (cons 0 0))) (k 0) (kk 0)) (while (< k 16) (aset in k (md5-pack md5-input kk)) (setq k (+ k 1) kk (+ kk 4))) (md5-transform in)))))) (defun md5-pack (array i) "Pack the four bytes at ARRAY reference I to I+3 into a 32-bit integer." (cons (+ (lsh (aref array (+ i 3)) 8) (aref array (+ i 2))) (+ (lsh (aref array (+ i 1)) 8) (aref array (+ i 0))))) (defun md5-byte (array n b) "Unpack byte B (0 to 3) from Nth member of ARRAY of 32-bit integers." (let ((e (aref array n))) (cond ((eq b 0) (logand 255 (cdr e))) ((eq b 1) (lsh (cdr e) -8)) ((eq b 2) (logand 255 (car e))) ((eq b 3) (lsh (car e) -8))))) (defun md5-final () (let ((in (make-vector 16 (cons 0 0))) (j 0) (digest (make-vector 16 0)) (padding)) ;; Save the number of bits in the message (aset in 14 (cons (aref md5-bits 1) (aref md5-bits 0))) (aset in 15 (cons (aref md5-bits 3) (aref md5-bits 2))) ;; Compute number of bytes modulo 64 (setq j (% (/ (aref md5-bits 0) 8) 64)) ;; Pad out computation to 56 bytes modulo 64 (setq padding (make-vector (if (< j 56) (- 56 j) (- 120 j)) 0)) (aset padding 0 128) (md5-update padding) ;; Append length in bits and transform (let ((k 0) (kk 0)) (while (< k 14) (aset in k (md5-pack md5-input kk)) (setq k (+ k 1) kk (+ kk 4)))) (md5-transform in) ;; Store the results in the digest (let ((k 0) (kk 0)) (while (< k 4) (aset digest (+ kk 0) (md5-byte md5-buffer k 0)) (aset digest (+ kk 1) (md5-byte md5-buffer k 1)) (aset digest (+ kk 2) (md5-byte md5-buffer k 2)) (aset digest (+ kk 3) (md5-byte md5-buffer k 3)) (setq k (+ k 1) kk (+ kk 4)))) ;; Return digest digest)) ;; It says in the RSA source, "Note that if the Mysterious Constants are ;; arranged backwards in little-endian order and decrypted with the DES ;; they produce OCCULT MESSAGES!" Security through obscurity? (defun md5-transform (in) "Basic MD5 step. Transform md5-buffer based on array IN." (let ((a (aref md5-buffer 0)) (b (aref md5-buffer 1)) (c (aref md5-buffer 2)) (d (aref md5-buffer 3))) (setq a (md5-FF a b c d (aref in 0) 7 '(55146 . 42104)) d (md5-FF d a b c (aref in 1) 12 '(59591 . 46934)) c (md5-FF c d a b (aref in 2) 17 '( 9248 . 28891)) b (md5-FF b c d a (aref in 3) 22 '(49597 . 52974)) a (md5-FF a b c d (aref in 4) 7 '(62844 . 4015)) d (md5-FF d a b c (aref in 5) 12 '(18311 . 50730)) c (md5-FF c d a b (aref in 6) 17 '(43056 . 17939)) b (md5-FF b c d a (aref in 7) 22 '(64838 . 38145)) a (md5-FF a b c d (aref in 8) 7 '(27008 . 39128)) d (md5-FF d a b c (aref in 9) 12 '(35652 . 63407)) c (md5-FF c d a b (aref in 10) 17 '(65535 . 23473)) b (md5-FF b c d a (aref in 11) 22 '(35164 . 55230)) a (md5-FF a b c d (aref in 12) 7 '(27536 . 4386)) d (md5-FF d a b c (aref in 13) 12 '(64920 . 29075)) c (md5-FF c d a b (aref in 14) 17 '(42617 . 17294)) b (md5-FF b c d a (aref in 15) 22 '(18868 . 2081)) a (md5-GG a b c d (aref in 1) 5 '(63006 . 9570)) d (md5-GG d a b c (aref in 6) 9 '(49216 . 45888)) c (md5-GG c d a b (aref in 11) 14 '( 9822 . 23121)) b (md5-GG b c d a (aref in 0) 20 '(59830 . 51114)) a (md5-GG a b c d (aref in 5) 5 '(54831 . 4189)) d (md5-GG d a b c (aref in 10) 9 '( 580 . 5203)) c (md5-GG c d a b (aref in 15) 14 '(55457 . 59009)) b (md5-GG b c d a (aref in 4) 20 '(59347 . 64456)) a (md5-GG a b c d (aref in 9) 5 '( 8673 . 52710)) d (md5-GG d a b c (aref in 14) 9 '(49975 . 2006)) c (md5-GG c d a b (aref in 3) 14 '(62677 . 3463)) b (md5-GG b c d a (aref in 8) 20 '(17754 . 5357)) a (md5-GG a b c d (aref in 13) 5 '(43491 . 59653)) d (md5-GG d a b c (aref in 2) 9 '(64751 . 41976)) c (md5-GG c d a b (aref in 7) 14 '(26479 . 729)) b (md5-GG b c d a (aref in 12) 20 '(36138 . 19594)) a (md5-HH a b c d (aref in 5) 4 '(65530 . 14658)) d (md5-HH d a b c (aref in 8) 11 '(34673 . 63105)) c (md5-HH c d a b (aref in 11) 16 '(28061 . 24866)) b (md5-HH b c d a (aref in 14) 23 '(64997 . 14348)) a (md5-HH a b c d (aref in 1) 4 '(42174 . 59972)) d (md5-HH d a b c (aref in 4) 11 '(19422 . 53161)) c (md5-HH c d a b (aref in 7) 16 '(63163 . 19296)) b (md5-HH b c d a (aref in 10) 23 '(48831 . 48240)) a (md5-HH a b c d (aref in 13) 4 '(10395 . 32454)) d (md5-HH d a b c (aref in 0) 11 '(60065 . 10234)) c (md5-HH c d a b (aref in 3) 16 '(54511 . 12421)) b (md5-HH b c d a (aref in 6) 23 '( 1160 . 7429)) a (md5-HH a b c d (aref in 9) 4 '(55764 . 53305)) d (md5-HH d a b c (aref in 12) 11 '(59099 . 39397)) c (md5-HH c d a b (aref in 15) 16 '( 8098 . 31992)) b (md5-HH b c d a (aref in 2) 23 '(50348 . 22117)) a (md5-II a b c d (aref in 0) 6 '(62505 . 8772)) d (md5-II d a b c (aref in 7) 10 '(17194 . 65431)) c (md5-II c d a b (aref in 14) 15 '(43924 . 9127)) b (md5-II b c d a (aref in 5) 21 '(64659 . 41017)) a (md5-II a b c d (aref in 12) 6 '(25947 . 22979)) d (md5-II d a b c (aref in 3) 10 '(36620 . 52370)) c (md5-II c d a b (aref in 10) 15 '(65519 . 62589)) b (md5-II b c d a (aref in 1) 21 '(34180 . 24017)) a (md5-II a b c d (aref in 8) 6 '(28584 . 32335)) d (md5-II d a b c (aref in 15) 10 '(65068 . 59104)) c (md5-II c d a b (aref in 6) 15 '(41729 . 17172)) b (md5-II b c d a (aref in 13) 21 '(19976 . 4513)) a (md5-II a b c d (aref in 4) 6 '(63315 . 32386)) d (md5-II d a b c (aref in 11) 10 '(48442 . 62005)) c (md5-II c d a b (aref in 2) 15 '(10967 . 53947)) b (md5-II b c d a (aref in 9) 21 '(60294 . 54161))) (aset md5-buffer 0 (md5-add (aref md5-buffer 0) a)) (aset md5-buffer 1 (md5-add (aref md5-buffer 1) b)) (aset md5-buffer 2 (md5-add (aref md5-buffer 2) c)) (aset md5-buffer 3 (md5-add (aref md5-buffer 3) d)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Here begins the merger with the XEmacs API and the md5.el from the URL ;;; package. Courtesy wmperry@cs.indiana.edu ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun md5 (object &optional start end) "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." (let ((buffer nil)) (unwind-protect (save-excursion (setq buffer (generate-new-buffer " *md5-work*")) (set-buffer buffer) (cond ((bufferp object) (insert-buffer-substring object start end)) ((stringp object) (insert (if (or start end) (substring object start end) object))) (t nil)) (prog1 (if (or (null md5-maximum-internal-length) (<= (point-max) md5-maximum-internal-length)) (mapconcat (function (lambda (node) (format "%02x" node))) (md5-encode (buffer-string)) "") (call-process-region (point-min) (point-max) shell-file-name t buffer nil shell-command-switch md5-program) ;; MD5 digest is 32 chars long ;; mddriver adds a newline to make neaten output for tty ;; viewing, make sure we leave it behind. (buffer-substring (point-min) (+ (point-min) 32))) (kill-buffer buffer))) (and buffer (buffer-name buffer) (kill-buffer buffer) nil)))) (provide 'md5-el)