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 
27     void clear();
28 
29     @property string name() const;
30 
31     /**
32     * Returns: hash block size as defined for this algorithm
33     */
34     @property size_t hashBlockSize() const;
35 }
36 
37 static if (BOTAN_TEST):
38 import botan.test;
39 
40 import botan.libstate.libstate;
41 import botan.codec.hex;
42 import core.atomic;
43 import memutils.hashmap;
44 
45 private shared size_t total_tests;
46 
47 size_t hashTest(string algo, string in_hex, string out_hex)
48 {
49     AlgorithmFactory af = globalState().algorithmFactory();
50     
51     const auto providers = af.providersOf(algo);
52     size_t fails = 0;
53     atomicOp!"+="(total_tests, cast(size_t)1);
54     if (providers.empty)
55     {
56         logTrace("Unknown algo " ~ algo);
57         ++fails;
58     }
59     
60     foreach (provider; providers[])
61     {
62         auto proto = af.prototypeHashFunction(algo, provider);
63 
64         atomicOp!"+="(total_tests, 1);
65 
66         if (!proto)
67         {
68             logError("Unable to get " ~ algo ~ " from " ~ provider);
69             ++fails;
70             continue;
71         }
72         
73         Unique!HashFunction hash = proto.clone();
74         auto decoded = hexDecode(in_hex);
75         hash.update(decoded);
76         
77         auto h = hash.finished();
78 
79         atomicOp!"+="(total_tests, 1);
80 
81         if (h != hexDecodeLocked(out_hex))
82         {
83             logError(algo ~ " " ~ provider ~ " got " ~ hexEncode(h) ~ " != " ~ out_hex);
84             ++fails;
85         }
86         
87         // Test to make sure clear() resets what we need it to
88         hash.update("some discarded input");
89         hash.clear();
90         
91         hash.update(hexDecode(in_hex));
92         
93         h = hash.finished();
94 
95         atomicOp!"+="(total_tests, 1);
96 
97         if (h != hexDecodeLocked(out_hex))
98         {
99             logError(algo ~ " " ~ provider ~ " got " ~ hexEncode(h) ~ " != " ~ out_hex);
100             ++fails;
101         }
102     }
103     
104     return fails;
105 }
106 
107 static if (BOTAN_HAS_TESTS && !SKIP_HASH_TEST) unittest
108 {
109     logDebug("Testing hash.d ...");
110     import botan.libstate.libstate : globalState;
111     globalState();
112     auto test = delegate(string input)
113     {
114         File vec = File(input, "r");
115 
116         return runTestsBb(vec, "Hash", "Out", true,
117             (ref HashMap!(string, string) m) {
118                 return hashTest(m["Hash"], m["In"], m["Out"]);
119             });
120     };
121     
122     size_t fails = runTestsInDir("test_data/hash", test);
123 
124     testReport("hash", total_tests, fails);
125 }