diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/hash/skein/skein_512.cpp')
-rw-r--r-- | comm/third_party/botan/src/lib/hash/skein/skein_512.cpp | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/hash/skein/skein_512.cpp b/comm/third_party/botan/src/lib/hash/skein/skein_512.cpp new file mode 100644 index 0000000000..edf95d5961 --- /dev/null +++ b/comm/third_party/botan/src/lib/hash/skein/skein_512.cpp @@ -0,0 +1,178 @@ +/* +* The Skein-512 hash function +* (C) 2009,2010,2014 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/skein_512.h> +#include <botan/loadstor.h> +#include <botan/exceptn.h> +#include <algorithm> + +namespace Botan { + +Skein_512::Skein_512(size_t arg_output_bits, + const std::string& arg_personalization) : + m_personalization(arg_personalization), + m_output_bits(arg_output_bits), + m_threefish(new Threefish_512), + m_T(2), m_buffer(64), m_buf_pos(0) + { + if(m_output_bits == 0 || m_output_bits % 8 != 0 || m_output_bits > 512) + throw Invalid_Argument("Bad output bits size for Skein-512"); + + initial_block(); + } + +std::string Skein_512::name() const + { + if(m_personalization != "") + return "Skein-512(" + std::to_string(m_output_bits) + "," + + m_personalization + ")"; + return "Skein-512(" + std::to_string(m_output_bits) + ")"; + } + +HashFunction* Skein_512::clone() const + { + return new Skein_512(m_output_bits, m_personalization); + } + +std::unique_ptr<HashFunction> Skein_512::copy_state() const + { + std::unique_ptr<Skein_512> copy(new Skein_512(m_output_bits, m_personalization)); + + copy->m_threefish->m_K = this->m_threefish->m_K; + copy->m_T = this->m_T; + copy->m_buffer = this->m_buffer; + copy->m_buf_pos = this->m_buf_pos; + + // work around GCC 4.8 bug + return std::unique_ptr<HashFunction>(copy.release()); + } + +void Skein_512::clear() + { + zeroise(m_buffer); + m_buf_pos = 0; + + initial_block(); + } + +void Skein_512::reset_tweak(type_code type, bool is_final) + { + m_T[0] = 0; + + m_T[1] = (static_cast<uint64_t>(type) << 56) | + (static_cast<uint64_t>(1) << 62) | + (static_cast<uint64_t>(is_final) << 63); + } + +void Skein_512::initial_block() + { + const uint8_t zeros[64] = { 0 }; + + m_threefish->set_key(zeros, sizeof(zeros)); + + // ASCII("SHA3") followed by version (0x0001) code + uint8_t config_str[32] = { 0x53, 0x48, 0x41, 0x33, 0x01, 0x00, 0 }; + store_le(uint32_t(m_output_bits), config_str + 8); + + reset_tweak(SKEIN_CONFIG, true); + ubi_512(config_str, sizeof(config_str)); + + if(m_personalization != "") + { + /* + This is a limitation of this implementation, and not of the + algorithm specification. Could be fixed relatively easily, but + doesn't seem worth the trouble. + */ + if(m_personalization.length() > 64) + throw Invalid_Argument("Skein personalization must be less than 64 bytes"); + + const uint8_t* bits = cast_char_ptr_to_uint8(m_personalization.data()); + reset_tweak(SKEIN_PERSONALIZATION, true); + ubi_512(bits, m_personalization.length()); + } + + reset_tweak(SKEIN_MSG, false); + } + +void Skein_512::ubi_512(const uint8_t msg[], size_t msg_len) + { + secure_vector<uint64_t> M(8); + + do + { + const size_t to_proc = std::min<size_t>(msg_len, 64); + m_T[0] += to_proc; + + load_le(M.data(), msg, to_proc / 8); + + if(to_proc % 8) + { + for(size_t j = 0; j != to_proc % 8; ++j) + M[to_proc/8] |= static_cast<uint64_t>(msg[8*(to_proc/8)+j]) << (8*j); + } + + m_threefish->skein_feedfwd(M, m_T); + + // clear first flag if set + m_T[1] &= ~(static_cast<uint64_t>(1) << 62); + + msg_len -= to_proc; + msg += to_proc; + } while(msg_len); + } + +void Skein_512::add_data(const uint8_t input[], size_t length) + { + if(length == 0) + return; + + if(m_buf_pos) + { + buffer_insert(m_buffer, m_buf_pos, input, length); + if(m_buf_pos + length > 64) + { + ubi_512(m_buffer.data(), m_buffer.size()); + + input += (64 - m_buf_pos); + length -= (64 - m_buf_pos); + m_buf_pos = 0; + } + } + + const size_t full_blocks = (length - 1) / 64; + + if(full_blocks) + ubi_512(input, 64*full_blocks); + + length -= full_blocks * 64; + + buffer_insert(m_buffer, m_buf_pos, input + full_blocks * 64, length); + m_buf_pos += length; + } + +void Skein_512::final_result(uint8_t out[]) + { + m_T[1] |= (static_cast<uint64_t>(1) << 63); // final block flag + + for(size_t i = m_buf_pos; i != m_buffer.size(); ++i) + m_buffer[i] = 0; + + ubi_512(m_buffer.data(), m_buf_pos); + + const uint8_t counter[8] = { 0 }; + + reset_tweak(SKEIN_OUTPUT, true); + ubi_512(counter, sizeof(counter)); + + copy_out_vec_le(out, m_output_bits / 8, m_threefish->m_K); + + m_buf_pos = 0; + initial_block(); + } + +} |