1 /**
2 * Comb4P hash combiner
3 * 
4 * Copyright:
5 * (C) 2010 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.comb4p;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_COMB4P):
15 
16 import botan.hash.hash;
17 import botan.utils.xor_buf;
18 import botan.utils.types;
19 import botan.utils.mem_ops;
20 import std.exception;
21 import std.algorithm : max, min;
22 
23 /**
24 * Combines two hash functions using a Feistel scheme. Described in
25 * "On the Security of Hash Function Combiners", Anja Lehmann
26 */
27 class Comb4P : HashFunction
28 {
29 public:
30     /**
31     * Params:
32     *  h1 = the first hash
33     *  h2 = the second hash
34     */
35     this(HashFunction h1, HashFunction h2)
36     {
37         m_hash1 = h1;
38         m_hash2 = h2;
39         if (m_hash1.name == m_hash2.name)
40             throw new InvalidArgument("Comb4P: Must use two distinct hashes");
41         
42         if (m_hash1.outputLength != m_hash2.outputLength)
43             throw new InvalidArgument("Comb4P: Incompatible hashes " ~
44                                        m_hash1.name ~ " and " ~
45                                        m_hash2.name);
46         
47         clear();
48     }
49 
50 
51     override @property size_t hashBlockSize() const
52     {
53         if (m_hash1.hashBlockSize == m_hash2.hashBlockSize)
54             return m_hash1.hashBlockSize;
55         
56         /*
57         * Return LCM of the block sizes? This would probably be OK for
58         * HMAC, which is the main thing relying on knowing the block size.
59         */
60         return 0;
61     }
62 
63     override @property size_t outputLength() const
64     {
65         return m_hash1.outputLength + m_hash2.outputLength;
66     }
67 
68     override HashFunction clone() const
69     {
70         return new Comb4P(m_hash1.clone(), m_hash2.clone());
71     }
72 
73     override @property string name() const
74     {
75         return "Comb4P(" ~ m_hash1.name ~ "," ~ m_hash2.name ~ ")";
76     }
77 
78     override void clear()
79     {
80         m_hash1.clear();
81         m_hash2.clear();
82         
83         // Prep for processing next message, if any
84         m_hash1.update(0);
85         m_hash2.update(0);
86     }
87 
88 protected:
89     override void addData(const(ubyte)* input, size_t length)
90     {
91         m_hash1.update(input, length);
92         m_hash2.update(input, length);
93     }
94 
95     override void finalResult(ubyte* output)
96     {
97         SecureVector!ubyte h1 = m_hash1.finished();
98         SecureVector!ubyte h2 = m_hash2.finished();
99         
100         // First round
101         xorBuf(h1.ptr, h2.ptr, min(h1.length, h2.length));
102         
103         // Second round
104         comb4p_round(h2, h1, 1, *m_hash1, *m_hash2);
105         
106         // Third round
107         comb4p_round(h1, h2, 2, *m_hash1, *m_hash2);
108         
109         copyMem(output            , h1.ptr, h1.length);
110         copyMem(output + h1.length, h2.ptr, h2.length);
111         
112         // Prep for processing next message, if any
113         m_hash1.update(0);
114         m_hash2.update(0);
115     }
116 
117     Unique!HashFunction m_hash1, m_hash2;
118 }
119 
120 private:
121     
122 void comb4p_round(ref SecureVector!ubyte output,
123                   const ref SecureVector!ubyte input,
124                   ubyte round_no,
125                   HashFunction h1,
126                   HashFunction h2)
127 {
128     h1.update(round_no);
129     h2.update(round_no);
130     
131     h1.update(input.ptr, input.length);
132     h2.update(input.ptr, input.length);
133     
134     SecureVector!ubyte h_buf = h1.finished();
135     xorBuf(output.ptr, h_buf.ptr, min(output.length, h_buf.length));
136     
137     h_buf = h2.finished();
138     xorBuf(output.ptr, h_buf.ptr, min(output.length, h_buf.length));
139 }