1 /**
2 * Hash Function Base Class
3 * 
4 * Copyright:
5 * (C) 1999-2008 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.hash.hash;
12 
13 import botan.constants;
14 import botan.algo_base.buf_comp;
15 
16 /**
17 * This class represents hash function (message digest) objects
18 */
19 interface HashFunction : BufferedComputation
20 {
21 public:
22     /**
23     * Returns: new object representing the same algorithm as this
24     */
25     HashFunction clone() const;
26     final @disable HashFunction dup() const;
27 
28     void clear();
29 
30     @property string name() const;
31 
32     /**
33     * Returns: hash block size as defined for this algorithm
34     */
35     @property size_t hashBlockSize() const;
36 }
37 
38 static if (BOTAN_TEST):
39 import botan.test;
40 
41 import botan.libstate.libstate;
42 import botan.codec.hex;
43 import core.atomic;
44 import memutils.hashmap;
45 
46 private shared size_t total_tests;
47 
48 size_t hashTest(string algo, string in_hex, string out_hex)
49 {
50     AlgorithmFactory af = globalState().algorithmFactory();
51     
52     const auto providers = af.providersOf(algo);
53     size_t fails = 0;
54     atomicOp!"+="(total_tests, cast(size_t)1);
55     if (providers.empty)
56     {
57         logTrace("Unknown algo " ~ algo);
58         ++fails;
59     }
60     
61     foreach (provider; providers[])
62     {
63         auto proto = af.prototypeHashFunction(algo, provider);
64 
65         atomicOp!"+="(total_tests, 1);
66 
67         if (!proto)
68         {
69             logError("Unable to get " ~ algo ~ " from " ~ provider);
70             ++fails;
71             continue;
72         }
73         
74         Unique!HashFunction hash = proto.clone();
75         auto decoded = hexDecode(in_hex);
76         hash.update(decoded);
77         
78         auto h = hash.finished();
79 
80         atomicOp!"+="(total_tests, 1);
81 
82         if (h != hexDecodeLocked(out_hex))
83         {
84             logError(algo ~ " " ~ provider ~ " got " ~ hexEncode(h) ~ " != " ~ out_hex);
85             ++fails;
86         }
87         
88         // Test to make sure clear() resets what we need it to
89         hash.update("some discarded input");
90         hash.clear();
91         
92         hash.update(hexDecode(in_hex));
93         
94         h = hash.finished();
95 
96         atomicOp!"+="(total_tests, 1);
97 
98         if (h != hexDecodeLocked(out_hex))
99         {
100             logError(algo ~ " " ~ provider ~ " got " ~ hexEncode(h) ~ " != " ~ out_hex);
101             ++fails;
102         }
103     }
104     
105     return fails;
106 }
107 
108 static if (BOTAN_HAS_TESTS && !SKIP_HASH_TEST) unittest
109 {
110     logDebug("Testing hash.d ...");
111     import botan.libstate.libstate : globalState;
112     globalState();
113     auto test = delegate(string input)
114     {
115         File vec = File(input, "r");
116 
117         return runTestsBb(vec, "Hash", "Out", true,
118             (ref HashMap!(string, string) m) {
119                 return hashTest(m["Hash"], m["In"], m["Out"]);
120             });
121     };
122     
123     size_t fails = runTestsInDir("test_data/hash", test);
124 
125     testReport("hash", total_tests, fails);
126 }