| 35628 |      1 | (*  Title:      Pure/General/sha1.ML
 | 
|  |      2 |     Author:     Makarius
 | 
|  |      3 | 
 | 
|  |      4 | Digesting strings according to SHA-1 (see RFC 3174) -- relatively slow
 | 
|  |      5 | version in pure ML.
 | 
|  |      6 | *)
 | 
|  |      7 | 
 | 
|  |      8 | signature SHA1 =
 | 
|  |      9 | sig
 | 
| 41954 |     10 |   eqtype digest
 | 
|  |     11 |   val digest: string -> digest
 | 
|  |     12 |   val rep: digest -> string
 | 
| 35628 |     13 | end;
 | 
|  |     14 | 
 | 
|  |     15 | structure SHA1: SHA1 =
 | 
|  |     16 | struct
 | 
|  |     17 | 
 | 
|  |     18 | (* 32bit words *)
 | 
|  |     19 | 
 | 
|  |     20 | infix 4 << >>;
 | 
|  |     21 | infix 3 andb;
 | 
|  |     22 | infix 2 orb xorb;
 | 
|  |     23 | 
 | 
|  |     24 | val op << = Word32.<<;
 | 
|  |     25 | val op >> = Word32.>>;
 | 
|  |     26 | val op andb = Word32.andb;
 | 
|  |     27 | val op orb = Word32.orb;
 | 
|  |     28 | val op xorb = Word32.xorb;
 | 
|  |     29 | val notb = Word32.notb;
 | 
|  |     30 | 
 | 
|  |     31 | fun rotate k w = w << k orb w >> (0w32 - k);
 | 
|  |     32 | 
 | 
|  |     33 | 
 | 
|  |     34 | (* hexadecimal words *)
 | 
|  |     35 | 
 | 
|  |     36 | fun hex_digit (text, w: Word32.word) =
 | 
|  |     37 |   let
 | 
|  |     38 |     val d = Word32.toInt (w andb 0wxf);
 | 
|  |     39 |     val dig = if d < 10 then chr (ord "0" + d) else chr (ord "a" + d - 10);
 | 
|  |     40 |   in (dig ^ text, w >> 0w4) end;
 | 
|  |     41 | 
 | 
|  |     42 | fun hex_word w = #1 (funpow 8 hex_digit ("", w));
 | 
|  |     43 | 
 | 
|  |     44 | 
 | 
|  |     45 | (* padding *)
 | 
|  |     46 | 
 | 
|  |     47 | fun pack_bytes 0 n = ""
 | 
|  |     48 |   | pack_bytes k n = pack_bytes (k - 1) (n div 256) ^ chr (n mod 256);
 | 
|  |     49 | 
 | 
|  |     50 | fun padded_text str =
 | 
|  |     51 |   let
 | 
|  |     52 |     val len = size str;
 | 
|  |     53 |     val padding = chr 128 ^ replicate_string (~ (len + 9) mod 64) (chr 0) ^ pack_bytes 8 (len * 8);
 | 
|  |     54 |     fun byte i = Char.ord (String.sub (if i < len then (str, i) else (padding, (i - len))));
 | 
|  |     55 |     fun word i =
 | 
|  |     56 |       Word32.fromInt (byte (4 * i)) << 0w24 orb
 | 
|  |     57 |       Word32.fromInt (byte (4 * i + 1)) << 0w16 orb
 | 
|  |     58 |       Word32.fromInt (byte (4 * i + 2)) << 0w8 orb
 | 
|  |     59 |       Word32.fromInt (byte (4 * i + 3));
 | 
|  |     60 |   in ((len + size padding) div 4, word) end;
 | 
|  |     61 | 
 | 
|  |     62 | 
 | 
| 41954 |     63 | (* digest_string *)
 | 
| 35628 |     64 | 
 | 
|  |     65 | fun digest_word (i, w, {a, b, c, d, e}) =
 | 
|  |     66 |   let
 | 
|  |     67 |     val {f, k} =
 | 
|  |     68 |       if i < 20 then
 | 
|  |     69 |         {f = (b andb c) orb (notb b andb d),
 | 
|  |     70 |          k = 0wx5A827999}
 | 
|  |     71 |       else if i < 40 then
 | 
|  |     72 |         {f = b xorb c xorb d,
 | 
|  |     73 |          k = 0wx6ED9EBA1}
 | 
|  |     74 |       else if i < 60 then
 | 
|  |     75 |         {f = (b andb c) orb (b andb d) orb (c andb d),
 | 
|  |     76 |          k = 0wx8F1BBCDC}
 | 
|  |     77 |       else
 | 
|  |     78 |         {f = b xorb c xorb d,
 | 
|  |     79 |          k = 0wxCA62C1D6};
 | 
|  |     80 |     val op + = Word32.+;
 | 
|  |     81 |   in
 | 
|  |     82 |     {a = rotate 0w5 a + f + e + w + k,
 | 
|  |     83 |      b = a,
 | 
|  |     84 |      c = rotate 0w30 b,
 | 
|  |     85 |      d = c,
 | 
|  |     86 |      e = d}
 | 
|  |     87 |   end;
 | 
|  |     88 | 
 | 
| 41954 |     89 | fun digest_string str =
 | 
| 35628 |     90 |   let
 | 
|  |     91 |     val (text_len, text) = padded_text str;
 | 
|  |     92 | 
 | 
|  |     93 |     (*hash result -- 5 words*)
 | 
|  |     94 |     val hash_array : Word32.word Array.array =
 | 
|  |     95 |       Array.fromList [0wx67452301, 0wxEFCDAB89, 0wx98BADCFE, 0wx10325476, 0wxC3D2E1F0];
 | 
|  |     96 |     fun hash i = Array.sub (hash_array, i);
 | 
|  |     97 |     fun add_hash x i = Array.update (hash_array, i, hash i + x);
 | 
|  |     98 | 
 | 
|  |     99 |     (*current chunk -- 80 words*)
 | 
|  |    100 |     val chunk_array = Array.array (80, 0w0: Word32.word);
 | 
|  |    101 |     fun chunk i = Array.sub (chunk_array, i);
 | 
|  |    102 |     fun init_chunk pos =
 | 
|  |    103 |       Array.modifyi (fn (i, _) =>
 | 
|  |    104 |         if i < 16 then text (pos + i)
 | 
|  |    105 |         else rotate 0w1 (chunk (i - 3) xorb chunk (i - 8) xorb chunk (i - 14) xorb chunk (i - 16)))
 | 
|  |    106 |       chunk_array;
 | 
|  |    107 | 
 | 
|  |    108 |     fun digest_chunks pos =
 | 
|  |    109 |       if pos < text_len then
 | 
|  |    110 |         let
 | 
|  |    111 |           val _ = init_chunk pos;
 | 
|  |    112 |           val {a, b, c, d, e} = Array.foldli digest_word
 | 
|  |    113 |             {a = hash 0,
 | 
|  |    114 |              b = hash 1,
 | 
|  |    115 |              c = hash 2,
 | 
|  |    116 |              d = hash 3,
 | 
|  |    117 |              e = hash 4}
 | 
|  |    118 |             chunk_array;
 | 
|  |    119 |           val _ = add_hash a 0;
 | 
|  |    120 |           val _ = add_hash b 1;
 | 
|  |    121 |           val _ = add_hash c 2;
 | 
|  |    122 |           val _ = add_hash d 3;
 | 
|  |    123 |           val _ = add_hash e 4;
 | 
|  |    124 |         in digest_chunks (pos + 16) end
 | 
|  |    125 |       else ();
 | 
|  |    126 |     val _  = digest_chunks 0;
 | 
|  |    127 | 
 | 
|  |    128 |     val hex = hex_word o hash;
 | 
|  |    129 |   in hex 0 ^ hex 1 ^ hex 2 ^ hex 3 ^ hex 4 end;
 | 
|  |    130 | 
 | 
| 41954 |    131 | 
 | 
|  |    132 | (* type digest *)
 | 
|  |    133 | 
 | 
|  |    134 | datatype digest = Digest of string;
 | 
|  |    135 | 
 | 
|  |    136 | val digest = Digest o digest_string;
 | 
|  |    137 | fun rep (Digest s) = s;
 | 
|  |    138 | 
 | 
| 35628 |    139 | end;
 |