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