Coverage Report

Created: 2026-03-31 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/hash/skein/skein_512.cpp
Line
Count
Source
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/mem_utils.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(std::span{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
      reset_tweak(SKEIN_PERSONALIZATION, true);
86
0
      ubi_512(as_span_of_bytes(m_personalization));
87
0
   }
88
89
0
   reset_tweak(SKEIN_MSG, false);
90
0
}
91
92
0
void Skein_512::ubi_512(std::span<const uint8_t> msg) {
93
0
   secure_vector<uint64_t> M(8);
94
95
   /* NOLINTNEXTLINE(*-avoid-do-while) */
96
0
   do {
97
0
      const size_t to_proc = std::min<size_t>(msg.size(), 64);
98
0
      m_T[0] += to_proc;
99
100
0
      load_le(M.data(), msg.data(), to_proc / 8);
101
102
0
      if(to_proc % 8 > 0) {
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 = msg.subspan(to_proc);
114
0
   } while(!msg.empty());
115
0
}
116
117
0
void Skein_512::add_data(std::span<const uint8_t> input) {
118
0
   BufferSlicer in(input);
119
120
0
   while(!in.empty()) {
121
0
      if(const auto one_block = m_buffer.handle_unaligned_data(in)) {
122
0
         ubi_512(one_block->data(), one_block->size());
123
0
      }
124
125
0
      if(m_buffer.in_alignment()) {
126
0
         const auto [aligned_data, full_blocks] = m_buffer.aligned_data_to_process(in);
127
0
         if(full_blocks > 0) {
128
0
            ubi_512(aligned_data.data(), aligned_data.size());
129
0
         }
130
0
      }
131
0
   }
132
0
}
133
134
0
void Skein_512::final_result(std::span<uint8_t> out) {
135
0
   m_T[1] |= (static_cast<uint64_t>(1) << 63);  // final block flag
136
137
0
   const auto pos = m_buffer.elements_in_buffer();
138
0
   m_buffer.fill_up_with_zeros();
139
0
   ubi_512(m_buffer.consume().data(), pos);
140
141
0
   std::array<uint8_t, 8> counter{};  // all zero
142
143
0
   reset_tweak(SKEIN_OUTPUT, true);
144
0
   ubi_512(counter);
145
146
0
   copy_out_le(out.first(m_output_bits / 8), m_threefish->m_K);
147
148
0
   initial_block();
149
0
}
150
151
}  // namespace Botan