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 }