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