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 }