1 /** 2 * The Skein-512 hash function 3 * 4 * Copyright: 5 * (C) 2009,2014 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.skein_512; 12 13 import botan.constants; 14 static if (BOTAN_HAS_SKEIN_512): 15 16 import botan.hash.hash; 17 import botan.block.threefish; 18 import botan.utils.loadstor; 19 import botan.utils.parsing; 20 import botan.utils.exceptn; 21 import botan.utils.xor_buf; 22 import botan.utils.types; 23 import botan.utils.get_byte; 24 import std.conv : to; 25 import std.algorithm; 26 27 /** 28 * Skein-512, a SHA-3 candidate 29 */ 30 final class Skein512 : HashFunction 31 { 32 public: 33 /** 34 * Params: 35 * arg_output_bits = the output size of Skein in bits 36 * arg_personalization = is a string that will paramaterize the 37 * hash output 38 */ 39 this(size_t arg_output_bits = 512, string arg_personalization = "") 40 { 41 m_personalization = arg_personalization; 42 m_output_bits = arg_output_bits; 43 m_threefish = new Threefish512; 44 m_T = SecureVector!ulong(2); 45 m_buffer = SecureVector!ubyte(64); 46 m_buf_pos = 0; 47 48 if (m_output_bits == 0 || m_output_bits % 8 != 0 || m_output_bits > 512) 49 throw new InvalidArgument("Bad output bits size for Skein-512"); 50 51 initialBlock(); 52 } 53 54 override @property size_t hashBlockSize() const { return 64; } 55 override @property size_t outputLength() const { return m_output_bits / 8; } 56 57 override HashFunction clone() const 58 { 59 return new Skein512(m_output_bits, m_personalization); 60 } 61 62 override @property string name() const 63 { 64 if (m_personalization != "") 65 return "Skein-512(" ~ to!string(m_output_bits) ~ "," ~ m_personalization ~ ")"; 66 return "Skein-512(" ~ to!string(m_output_bits) ~ ")"; 67 } 68 69 override void clear() 70 { 71 zeroise(m_buffer); 72 m_buf_pos = 0; 73 74 initialBlock(); 75 } 76 77 protected: 78 enum type_code { 79 SKEIN_KEY = 0, 80 SKEIN_CONFIG = 4, 81 SKEIN_PERSONALIZATION = 8, 82 SKEIN_PUBLIC_KEY = 12, 83 SKEIN_KEY_IDENTIFIER = 16, 84 SKEIN_NONCE = 20, 85 SKEIN_MSG = 48, 86 SKEIN_OUTPUT = 63 87 } 88 89 override void addData(const(ubyte)* input, size_t length) 90 { 91 if (length == 0) 92 return; 93 94 if (m_buf_pos) 95 { 96 bufferInsert(m_buffer, m_buf_pos, input, length); 97 if (m_buf_pos + length > 64) 98 { 99 ubi_512(m_buffer.ptr, m_buffer.length); 100 101 input += (64 - m_buf_pos); 102 length -= (64 - m_buf_pos); 103 m_buf_pos = 0; 104 } 105 } 106 107 const size_t full_blocks = (length - 1) / 64; 108 109 if (full_blocks) 110 ubi_512(input, 64*full_blocks); 111 112 length -= full_blocks * 64; 113 114 bufferInsert(m_buffer, m_buf_pos, input + full_blocks * 64, length); 115 m_buf_pos += length; 116 } 117 118 override void finalResult(ubyte* output) 119 { 120 m_T[1] |= ((cast(ulong)1) << 63); // final block flag 121 122 foreach (size_t i; m_buf_pos .. m_buffer.length) 123 m_buffer[i] = 0; 124 125 ubi_512(m_buffer.ptr, m_buf_pos); 126 127 const ubyte[8] counter; 128 129 resetTweak(type_code.SKEIN_OUTPUT, true); 130 ubi_512(counter.ptr, counter.length); 131 132 const size_t out_bytes = m_output_bits / 8; 133 134 foreach (size_t i; 0 .. out_bytes) 135 output[i] = get_byte(7-i%8, m_threefish.m_K[i/8]); 136 137 m_buf_pos = 0; 138 initialBlock(); 139 } 140 141 void ubi_512(const(ubyte)* msg, size_t msg_len) 142 { 143 SecureVector!ulong M = SecureVector!ulong(8); 144 145 do 146 { 147 const size_t to_proc = std.algorithm.min(msg_len, 64); 148 m_T[0] += to_proc; 149 150 loadLittleEndian(M.ptr, msg, to_proc / 8); 151 152 if (to_proc % 8) 153 { 154 foreach (size_t j; 0 .. (to_proc % 8)) 155 M[to_proc/8] |= cast(ulong)(msg[8*(to_proc/8)+j]) << (8*j); 156 } 157 158 m_threefish.skeinFeedfwd(M, m_T); 159 160 // clear first flag if set 161 m_T[1] &= ~(cast(ulong)(1) << 62); 162 163 msg_len -= to_proc; 164 msg += to_proc; 165 } while (msg_len); 166 } 167 168 169 void initialBlock() 170 { 171 const ubyte[64] zeros; 172 173 m_threefish.setKey(zeros.ptr, zeros.length); 174 175 // ASCII("SHA3") followed by version (0x0001) code 176 ubyte[32] config_str; 177 config_str[0 .. 7] = [0x53, 0x48, 0x41, 0x33, 0x01, 0x00, 0 ]; 178 storeLittleEndian(cast(uint) m_output_bits, config_str.ptr + 8); 179 180 resetTweak(type_code.SKEIN_CONFIG, true); 181 ubi_512(config_str.ptr, config_str.length); 182 183 if (m_personalization != "") 184 { 185 /* 186 This is a limitation of this implementation, and not of the 187 algorithm specification. Could be fixed relatively easily, but 188 doesn't seem worth the trouble. 189 */ 190 if (m_personalization.length > 64) 191 throw new InvalidArgument("Skein m_personalization must be less than 64 bytes"); 192 193 const(ubyte)* bits = cast(const(ubyte)*)(m_personalization.ptr); 194 resetTweak(type_code.SKEIN_PERSONALIZATION, true); 195 ubi_512(bits, m_personalization.length); 196 } 197 198 resetTweak(type_code.SKEIN_MSG, false); 199 } 200 201 void resetTweak(type_code type, bool fin) 202 { 203 m_T[0] = 0; 204 205 m_T[1] = (cast(ulong)(type) << 56) | 206 (cast(ulong)(1) << 62) | 207 (cast(ulong)(fin) << 63); 208 } 209 210 string m_personalization; 211 size_t m_output_bits; 212 213 Unique!Threefish512 m_threefish; 214 SecureVector!ulong m_T; 215 SecureVector!ubyte m_buffer; 216 size_t m_buf_pos; 217 }