/src/botan/src/lib/hash/skein/skein_512.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * The Skein-512 hash function |
3 | | * (C) 2009,2010,2014 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/internal/skein_512.h> |
9 | | |
10 | | #include <botan/exceptn.h> |
11 | | #include <botan/internal/fmt.h> |
12 | | #include <botan/internal/loadstor.h> |
13 | | #include <botan/internal/stl_util.h> |
14 | | #include <algorithm> |
15 | | |
16 | | namespace Botan { |
17 | | |
18 | | Skein_512::Skein_512(size_t arg_output_bits, std::string_view arg_personalization) : |
19 | 0 | m_personalization(arg_personalization), |
20 | 0 | m_output_bits(arg_output_bits), |
21 | 0 | m_threefish(std::make_unique<Threefish_512>()), |
22 | 0 | m_T(2) { |
23 | 0 | if(m_output_bits == 0 || m_output_bits % 8 != 0 || m_output_bits > 512) { |
24 | 0 | throw Invalid_Argument("Bad output bits size for Skein-512"); |
25 | 0 | } |
26 | | |
27 | 0 | initial_block(); |
28 | 0 | } |
29 | | |
30 | 0 | std::string Skein_512::name() const { |
31 | 0 | if(m_personalization.empty()) { |
32 | 0 | return fmt("Skein-512({})", m_output_bits); |
33 | 0 | } else { |
34 | 0 | return fmt("Skein-512({},{})", m_output_bits, m_personalization); |
35 | 0 | } |
36 | 0 | } |
37 | | |
38 | 0 | std::unique_ptr<HashFunction> Skein_512::new_object() const { |
39 | 0 | return std::make_unique<Skein_512>(m_output_bits, m_personalization); |
40 | 0 | } |
41 | | |
42 | 0 | std::unique_ptr<HashFunction> Skein_512::copy_state() const { |
43 | 0 | auto copy = std::make_unique<Skein_512>(m_output_bits, m_personalization); |
44 | 0 | copy->m_threefish->m_K = this->m_threefish->m_K; |
45 | 0 | copy->m_T = this->m_T; |
46 | 0 | copy->m_buffer = this->m_buffer; |
47 | 0 | return copy; |
48 | 0 | } |
49 | | |
50 | 0 | void Skein_512::clear() { |
51 | 0 | m_buffer.clear(); |
52 | |
|
53 | 0 | initial_block(); |
54 | 0 | } |
55 | | |
56 | 0 | void Skein_512::reset_tweak(type_code type, bool is_final) { |
57 | 0 | m_T[0] = 0; |
58 | |
|
59 | 0 | m_T[1] = |
60 | 0 | (static_cast<uint64_t>(type) << 56) | (static_cast<uint64_t>(1) << 62) | (static_cast<uint64_t>(is_final) << 63); |
61 | 0 | } |
62 | | |
63 | 0 | void Skein_512::initial_block() { |
64 | 0 | const uint8_t zeros[64] = {0}; |
65 | |
|
66 | 0 | m_threefish->set_key(zeros, sizeof(zeros)); |
67 | | |
68 | | // ASCII("SHA3") followed by version (0x0001) code |
69 | 0 | uint8_t config_str[32] = {0x53, 0x48, 0x41, 0x33, 0x01, 0x00, 0}; |
70 | 0 | store_le(uint32_t(m_output_bits), config_str + 8); |
71 | |
|
72 | 0 | reset_tweak(SKEIN_CONFIG, true); |
73 | 0 | ubi_512(config_str, sizeof(config_str)); |
74 | |
|
75 | 0 | if(!m_personalization.empty()) { |
76 | | /* |
77 | | This is a limitation of this implementation, and not of the |
78 | | algorithm specification. Could be fixed relatively easily, but |
79 | | doesn't seem worth the trouble. |
80 | | */ |
81 | 0 | if(m_personalization.length() > 64) { |
82 | 0 | throw Invalid_Argument("Skein personalization must be less than 64 bytes"); |
83 | 0 | } |
84 | | |
85 | 0 | const uint8_t* bits = cast_char_ptr_to_uint8(m_personalization.data()); |
86 | 0 | reset_tweak(SKEIN_PERSONALIZATION, true); |
87 | 0 | ubi_512(bits, m_personalization.length()); |
88 | 0 | } |
89 | | |
90 | 0 | reset_tweak(SKEIN_MSG, false); |
91 | 0 | } |
92 | | |
93 | 0 | void Skein_512::ubi_512(const uint8_t msg[], size_t msg_len) { |
94 | 0 | secure_vector<uint64_t> M(8); |
95 | |
|
96 | 0 | do { |
97 | 0 | const size_t to_proc = std::min<size_t>(msg_len, 64); |
98 | 0 | m_T[0] += to_proc; |
99 | |
|
100 | 0 | load_le(M.data(), msg, to_proc / 8); |
101 | |
|
102 | 0 | if(to_proc % 8) { |
103 | 0 | for(size_t j = 0; j != to_proc % 8; ++j) { |
104 | 0 | M[to_proc / 8] |= static_cast<uint64_t>(msg[8 * (to_proc / 8) + j]) << (8 * j); |
105 | 0 | } |
106 | 0 | } |
107 | |
|
108 | 0 | m_threefish->skein_feedfwd(M, m_T); |
109 | | |
110 | | // clear first flag if set |
111 | 0 | m_T[1] &= ~(static_cast<uint64_t>(1) << 62); |
112 | |
|
113 | 0 | msg_len -= to_proc; |
114 | 0 | msg += to_proc; |
115 | 0 | } while(msg_len); |
116 | 0 | } |
117 | | |
118 | 0 | void Skein_512::add_data(std::span<const uint8_t> input) { |
119 | 0 | BufferSlicer in(input); |
120 | |
|
121 | 0 | while(!in.empty()) { |
122 | 0 | if(const auto one_block = m_buffer.handle_unaligned_data(in)) { |
123 | 0 | ubi_512(one_block->data(), one_block->size()); |
124 | 0 | } |
125 | |
|
126 | 0 | if(m_buffer.in_alignment()) { |
127 | 0 | const auto [aligned_data, full_blocks] = m_buffer.aligned_data_to_process(in); |
128 | 0 | if(full_blocks > 0) { |
129 | 0 | ubi_512(aligned_data.data(), aligned_data.size()); |
130 | 0 | } |
131 | 0 | } |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | 0 | void Skein_512::final_result(std::span<uint8_t> out) { |
136 | 0 | m_T[1] |= (static_cast<uint64_t>(1) << 63); // final block flag |
137 | |
|
138 | 0 | const auto pos = m_buffer.elements_in_buffer(); |
139 | 0 | m_buffer.fill_up_with_zeros(); |
140 | 0 | ubi_512(m_buffer.consume().data(), pos); |
141 | |
|
142 | 0 | const uint8_t counter[8] = {0}; |
143 | |
|
144 | 0 | reset_tweak(SKEIN_OUTPUT, true); |
145 | 0 | ubi_512(counter, sizeof(counter)); |
146 | |
|
147 | 0 | copy_out_le(out.first(m_output_bits / 8), m_threefish->m_K); |
148 | |
|
149 | 0 | initial_block(); |
150 | 0 | } |
151 | | |
152 | | } // namespace Botan |