| author | wenzelm | 
| Sun, 14 Jun 2015 23:22:08 +0200 | |
| changeset 60477 | 051b200f7578 | 
| parent 57638 | ed58e740a699 | 
| child 62663 | bea354f6ff21 | 
| permissions | -rw-r--r-- | 
| 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 | |
| 57638 
ed58e740a699
less authentic SHA1.digest: trust Scala side on blobs and avoid re-calculation via Foreign Language Interface, which might be a cause of problems;
 wenzelm parents: 
41954diff
changeset | 13 | val fake: string -> digest | 
| 35628 | 14 | end; | 
| 15 | ||
| 16 | structure SHA1: SHA1 = | |
| 17 | struct | |
| 18 | ||
| 19 | (* 32bit words *) | |
| 20 | ||
| 21 | infix 4 << >>; | |
| 22 | infix 3 andb; | |
| 23 | infix 2 orb xorb; | |
| 24 | ||
| 25 | val op << = Word32.<<; | |
| 26 | val op >> = Word32.>>; | |
| 27 | val op andb = Word32.andb; | |
| 28 | val op orb = Word32.orb; | |
| 29 | val op xorb = Word32.xorb; | |
| 30 | val notb = Word32.notb; | |
| 31 | ||
| 32 | fun rotate k w = w << k orb w >> (0w32 - k); | |
| 33 | ||
| 34 | ||
| 35 | (* hexadecimal words *) | |
| 36 | ||
| 37 | fun hex_digit (text, w: Word32.word) = | |
| 38 | let | |
| 39 | val d = Word32.toInt (w andb 0wxf); | |
| 40 | val dig = if d < 10 then chr (ord "0" + d) else chr (ord "a" + d - 10); | |
| 41 | in (dig ^ text, w >> 0w4) end; | |
| 42 | ||
| 43 | fun hex_word w = #1 (funpow 8 hex_digit ("", w));
 | |
| 44 | ||
| 45 | ||
| 46 | (* padding *) | |
| 47 | ||
| 48 | fun pack_bytes 0 n = "" | |
| 49 | | pack_bytes k n = pack_bytes (k - 1) (n div 256) ^ chr (n mod 256); | |
| 50 | ||
| 51 | fun padded_text str = | |
| 52 | let | |
| 53 | val len = size str; | |
| 54 | val padding = chr 128 ^ replicate_string (~ (len + 9) mod 64) (chr 0) ^ pack_bytes 8 (len * 8); | |
| 55 | fun byte i = Char.ord (String.sub (if i < len then (str, i) else (padding, (i - len)))); | |
| 56 | fun word i = | |
| 57 | Word32.fromInt (byte (4 * i)) << 0w24 orb | |
| 58 | Word32.fromInt (byte (4 * i + 1)) << 0w16 orb | |
| 59 | Word32.fromInt (byte (4 * i + 2)) << 0w8 orb | |
| 60 | Word32.fromInt (byte (4 * i + 3)); | |
| 61 | in ((len + size padding) div 4, word) end; | |
| 62 | ||
| 63 | ||
| 41954 | 64 | (* digest_string *) | 
| 35628 | 65 | |
| 66 | fun digest_word (i, w, {a, b, c, d, e}) =
 | |
| 67 | let | |
| 68 |     val {f, k} =
 | |
| 69 | if i < 20 then | |
| 70 |         {f = (b andb c) orb (notb b andb d),
 | |
| 71 | k = 0wx5A827999} | |
| 72 | else if i < 40 then | |
| 73 |         {f = b xorb c xorb d,
 | |
| 74 | k = 0wx6ED9EBA1} | |
| 75 | else if i < 60 then | |
| 76 |         {f = (b andb c) orb (b andb d) orb (c andb d),
 | |
| 77 | k = 0wx8F1BBCDC} | |
| 78 | else | |
| 79 |         {f = b xorb c xorb d,
 | |
| 80 | k = 0wxCA62C1D6}; | |
| 81 | val op + = Word32.+; | |
| 82 | in | |
| 83 |     {a = rotate 0w5 a + f + e + w + k,
 | |
| 84 | b = a, | |
| 85 | c = rotate 0w30 b, | |
| 86 | d = c, | |
| 87 | e = d} | |
| 88 | end; | |
| 89 | ||
| 41954 | 90 | fun digest_string str = | 
| 35628 | 91 | let | 
| 92 | val (text_len, text) = padded_text str; | |
| 93 | ||
| 94 | (*hash result -- 5 words*) | |
| 95 | val hash_array : Word32.word Array.array = | |
| 96 | Array.fromList [0wx67452301, 0wxEFCDAB89, 0wx98BADCFE, 0wx10325476, 0wxC3D2E1F0]; | |
| 97 | fun hash i = Array.sub (hash_array, i); | |
| 98 | fun add_hash x i = Array.update (hash_array, i, hash i + x); | |
| 99 | ||
| 100 | (*current chunk -- 80 words*) | |
| 101 | val chunk_array = Array.array (80, 0w0: Word32.word); | |
| 102 | fun chunk i = Array.sub (chunk_array, i); | |
| 103 | fun init_chunk pos = | |
| 104 | Array.modifyi (fn (i, _) => | |
| 105 | if i < 16 then text (pos + i) | |
| 106 | else rotate 0w1 (chunk (i - 3) xorb chunk (i - 8) xorb chunk (i - 14) xorb chunk (i - 16))) | |
| 107 | chunk_array; | |
| 108 | ||
| 109 | fun digest_chunks pos = | |
| 110 | if pos < text_len then | |
| 111 | let | |
| 112 | val _ = init_chunk pos; | |
| 113 |           val {a, b, c, d, e} = Array.foldli digest_word
 | |
| 114 |             {a = hash 0,
 | |
| 115 | b = hash 1, | |
| 116 | c = hash 2, | |
| 117 | d = hash 3, | |
| 118 | e = hash 4} | |
| 119 | chunk_array; | |
| 120 | val _ = add_hash a 0; | |
| 121 | val _ = add_hash b 1; | |
| 122 | val _ = add_hash c 2; | |
| 123 | val _ = add_hash d 3; | |
| 124 | val _ = add_hash e 4; | |
| 125 | in digest_chunks (pos + 16) end | |
| 126 | else (); | |
| 127 | val _ = digest_chunks 0; | |
| 128 | ||
| 129 | val hex = hex_word o hash; | |
| 130 | in hex 0 ^ hex 1 ^ hex 2 ^ hex 3 ^ hex 4 end; | |
| 131 | ||
| 41954 | 132 | |
| 133 | (* type digest *) | |
| 134 | ||
| 135 | datatype digest = Digest of string; | |
| 136 | ||
| 137 | val digest = Digest o digest_string; | |
| 138 | fun rep (Digest s) = s; | |
| 139 | ||
| 57638 
ed58e740a699
less authentic SHA1.digest: trust Scala side on blobs and avoid re-calculation via Foreign Language Interface, which might be a cause of problems;
 wenzelm parents: 
41954diff
changeset | 140 | val fake = Digest; | 
| 
ed58e740a699
less authentic SHA1.digest: trust Scala side on blobs and avoid re-calculation via Foreign Language Interface, which might be a cause of problems;
 wenzelm parents: 
41954diff
changeset | 141 | |
| 35628 | 142 | end; |