/* * The Skein-512 hash function * (C) 2009,2010,2014 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include 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 Skein_512::copy_state() const { std::unique_ptr 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; return std::move(copy); } 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(type) << 56) | (static_cast(1) << 62) | (static_cast(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 = reinterpret_cast(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 M(8); do { const size_t to_proc = std::min(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(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(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(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(); } }