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 }