1 module dcrypt.crypto.digest; 2 3 import std.range: isOutputRange; 4 import std.range: OutputRange; 5 6 // TODO compatibility with std.digest? 7 8 template isDigest(T) 9 { 10 import std.digest.digest: isStdDigest = isDigest; 11 12 enum bool isDigest = 13 isStdDigest!T && 14 is(T == struct) && 15 isOutputRange!(T, ubyte) && isOutputRange!(T, const(ubyte)[]) && 16 is(typeof( 17 { 18 ubyte[] data; 19 T dig = void; // can define 20 dig.start(); // can reset the digest 21 22 dig.put(cast(ubyte)0); // can add a single byte 23 dig.put(cast(ubyte)0, cast(ubyte)0); // variadic function 24 dig.put(cast(const (ubyte)[]) data); // can add bytes 25 26 uint len = dig.doFinal(data); // can extract the hash value 27 auto value = dig.finish(); // has finish 28 29 uint digestSize = T.digestLength; // knows the length of the hash value in bytes 30 uint byteLength = T.byteLength; // knows the length of its internal state 31 uint blockSize = T.blockSize; // knows the size if its blocks 32 string name = T.name; // knows its own name 33 34 })); 35 } 36 37 /// Calculate the final hash value. 38 /// Returns: the hash value 39 mixin template finish() { 40 @safe @nogc nothrow 41 ubyte[digestLength] finish() { 42 ubyte[digestLength] buf; 43 doFinal(buf); 44 return buf; 45 } 46 } 47 48 @safe 49 public abstract class Digest { 50 51 @safe @property 52 public string name() pure nothrow; 53 54 /** 55 * return the size, in bytes, of the digest produced by this message digest. 56 * 57 * Returns the size, in bytes, of the digest produced by this message digest. 58 */ 59 @safe 60 public uint getDigestSize() pure nothrow @nogc; 61 62 63 /** 64 Return the size in bytes of the internal buffer the digest applies it's compression 65 function to. 66 Returns: the size of the internal state in bytes 67 */ 68 @safe 69 public uint getByteLength() pure nothrow @nogc; 70 71 72 /** 73 Used for padding (i.e. in HMacs) 74 Returns: the block size or 0 if the Digest is not block based 75 */ 76 @safe 77 public uint blockSize() pure nothrow @nogc; 78 79 /** 80 * update the message digest with a block of bytes. 81 * 82 * Params: 83 * input the ubyte slice containing the data. 84 */ 85 @safe 86 public void put(in ubyte[] input...) nothrow; 87 88 /** 89 * close the digest, producing the final digest value. The doFinal 90 * call leaves the digest reset. 91 * Returns: the amount of bytes written to output 92 */ 93 @safe 94 public uint doFinal(ubyte[] output) nothrow; 95 96 /** 97 * close the digest, producing the final digest value. The doFinal 98 * call leaves the digest reset. */ 99 @safe 100 public final ubyte[] doFinal() nothrow { 101 ubyte[] output = new ubyte[getDigestSize()]; 102 doFinal(output); 103 return output; 104 } 105 106 /** 107 * reset the digest back to it's initial state. 108 */ 109 @safe 110 public void start() nothrow; 111 112 /// create an independant copy of this Digest and it's full state 113 @safe @property 114 public Digest dup() nothrow; 115 116 } 117 118 119 @safe 120 public class WrapperDigest(T): Digest 121 if(isDigest!T) { 122 123 private T digest; 124 125 /** 126 * update the message digest with a block of bytes. 127 * 128 * Params: 129 * input = the ubyte slice containing the data. 130 */ 131 @safe 132 override public void put(in ubyte[] input...) nothrow @nogc { 133 digest.put(input); 134 } 135 136 /// Returns: The name of the digest algorithm. 137 @safe @property 138 public override string name() pure nothrow @nogc { 139 return T.name; 140 } 141 142 /** 143 * return the size, in bytes, of the digest produced by this message digest. 144 * 145 * Returns the size, in bytes, of the digest produced by this message digest. 146 */ 147 @safe @property 148 public override uint getDigestSize() pure nothrow @nogc { 149 return T.digestLength; 150 } 151 152 153 /** 154 Used for padding (i.e. in HMacs) 155 Returns: the block size or 0 if the Digest is not block based 156 */ 157 @safe 158 public override uint blockSize() pure nothrow @nogc { 159 return T.blockSize; 160 } 161 162 /// Return the size in bytes of the internal buffer the digest applies it's compression 163 /// function to. 164 /// Returns: the size of the internal state in bytes 165 @safe 166 public override uint getByteLength() pure nothrow @nogc { 167 return T.byteLength; 168 } 169 170 171 172 /** 173 * close the digest, producing the final digest value. The doFinal 174 * call leaves the digest reset. 175 * Returns: the amount of bytes written to output 176 */ 177 @safe 178 public override uint doFinal(ubyte[] output) nothrow @nogc { 179 return digest.doFinal(output); 180 } 181 182 /** 183 * reset the digest back to it's initial state. 184 */ 185 @safe 186 public override void start() nothrow @nogc { 187 digest.start(); 188 } 189 190 /// Create an independant copy of this Digest and it's full state. 191 @safe @property 192 public override Digest dup() nothrow { 193 WrapperDigest!T clone = new WrapperDigest!T; 194 195 clone.digest = this.digest; 196 197 return clone; 198 } 199 200 } 201 202 version(unittest) { 203 204 // unittest helper functions 205 206 /// Use this to tests Digest d with given input data and reference hashes. 207 /// 208 /// Params: 209 /// data = input for hash 210 /// hashes = expected hashes 211 /// 212 /// Throws: 213 /// AssertionError if generated hash != expected hash 214 @safe 215 public void testDigest(Digest d, in string[] plaintext, in string[] hashes) { 216 import dcrypt.util.encoders.hex; 217 import std.conv: text; 218 219 foreach (i; 0 .. plaintext.length) 220 { 221 const ubyte[] data = cast(const ubyte[])plaintext[i]; 222 const ubyte[] expectedHash = cast(const ubyte[])hashes[i]; 223 224 d.start(); 225 226 Digest clone = null; 227 228 if(data.length > 1) { 229 d.put(data[0..1]); 230 231 clone = d.dup; 232 assert(clone !is d, text(d.name, ".dup did not return an independent Digest")); 233 234 // update d an the clone with the remaining data 235 clone.put(data[1..$]); 236 d.put(data[1..$]); 237 }else { 238 d.put(data); 239 } 240 241 ubyte[] hash = d.doFinal(); 242 assert(hash == expectedHash, text(d.name, " failed: ",hexEncode(hash), " != ", hexEncode(hashes[i]))); 243 244 // the clone should now create the same hash 245 if(clone) { 246 hash = clone.doFinal(); 247 assert(hash == expectedHash, text(d.name, "dup() did not create an independant clone")); 248 } 249 } 250 } 251 }