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 }