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 }