• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 5079590438

25 May 2023 12:28PM UTC coverage: 92.228% (+0.5%) from 91.723%
5079590438

Pull #3502

github

Pull Request #3502: Apply clang-format to the codebase

75589 of 81959 relevant lines covered (92.23%)

12139530.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.75
/src/lib/tls/tls13/tls_transcript_hash_13.cpp
1
/*
2
* TLS transcript hash implementation for TLS 1.3
3
* (C) 2022 Jack Lloyd
4
*     2022 Hannes Rantzsch, René Meusel - neXenio GmbH
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include <botan/internal/tls_transcript_hash_13.h>
10

11
#include <botan/tls_exceptn.h>
12
#include <botan/tls_messages.h>
13
#include <botan/internal/tls_reader.h>
14

15
#include <utility>
16

17
namespace Botan::TLS {
18

19
Transcript_Hash_State::Transcript_Hash_State(std::string_view algo_spec) { set_algorithm(algo_spec); }
132✔
20

21
Transcript_Hash_State::Transcript_Hash_State(const Transcript_Hash_State& other) :
102✔
22
      m_hash((other.m_hash != nullptr) ? other.m_hash->copy_state() : nullptr),
102✔
23
      m_unprocessed_transcript(other.m_unprocessed_transcript),
102✔
24
      m_current(other.m_current),
102✔
25
      m_previous(other.m_previous),
102✔
26
      m_truncated(other.m_truncated) {}
102✔
27

28
Transcript_Hash_State Transcript_Hash_State::recreate_after_hello_retry_request(
71✔
29
   std::string_view algo_spec, const Transcript_Hash_State& prev_transcript_hash_state) {
30
   // make sure that we have seen exactly 'client_hello' and 'hello_retry_request'
31
   // before re-creating the transcript hash state
32
   BOTAN_STATE_CHECK(prev_transcript_hash_state.m_hash == nullptr);
71✔
33
   BOTAN_STATE_CHECK(prev_transcript_hash_state.m_unprocessed_transcript.size() == 2);
71✔
34

35
   Transcript_Hash_State ths(algo_spec);
71✔
36

37
   const auto& client_hello_1 = prev_transcript_hash_state.m_unprocessed_transcript.front();
71✔
38
   const auto& hello_retry_request = prev_transcript_hash_state.m_unprocessed_transcript.back();
71✔
39

40
   const size_t hash_length = ths.m_hash->output_length();
71✔
41
   BOTAN_ASSERT_NOMSG(hash_length < 256);
71✔
42

43
   // RFC 8446 4.4.1
44
   //    [...], when the server responds to a ClientHello with a HelloRetryRequest,
45
   //    the value of ClientHello1 is replaced with a special synthetic handshake
46
   //    message of handshake type "message_hash" [(0xFE)] containing:
47
   std::vector<uint8_t> message_hash;
71✔
48
   message_hash.reserve(4 + hash_length);
71✔
49
   message_hash.push_back(0xFE /* message type 'message_hash' RFC 8446 4. */);
71✔
50
   message_hash.push_back(0x00);
71✔
51
   message_hash.push_back(0x00);
71✔
52
   message_hash.push_back(static_cast<uint8_t>(hash_length));
71✔
53
   message_hash += ths.m_hash->process(client_hello_1);
142✔
54

55
   ths.update(message_hash);
71✔
56
   ths.update(hello_retry_request);
71✔
57

58
   return ths;
71✔
59
}
71✔
60

61
namespace {
62

63
// TODO: This is a massive code duplication of the client hello parsing code,
64
//       as well as basic parsing of extensions. We should resolve this.
65
//
66
// Ad-hoc idea: When parsing the production objects, we could keep markers into
67
//              the original buffer. E.g. the PSK extensions would keep its off-
68
//              set into the entire client hello buffer. Using that offset we
69
//              could quickly identify the offset of the binders list slice the
70
//              buffer without re-parsing it.
71
//
72
// Finds the truncation offset in a serialization of Client Hello as defined in
73
// RFC 8446 4.2.11.2 used for the calculation of PSK binder MACs.
74
size_t find_client_hello_truncation_mark(std::span<const uint8_t> client_hello) {
888✔
75
   TLS_Data_Reader reader("Client Hello Truncation", client_hello);
888✔
76

77
   // handshake message type
78
   BOTAN_ASSERT_NOMSG(reader.get_byte() == static_cast<uint8_t>(Handshake_Type::ClientHello));
888✔
79

80
   // message length
81
   reader.discard_next(3);
888✔
82

83
   // legacy version
84
   reader.discard_next(2);
888✔
85

86
   // random
87
   reader.discard_next(32);
888✔
88

89
   // session ID
90
   const auto session_id_length = reader.get_byte();
888✔
91
   reader.discard_next(session_id_length);
888✔
92

93
   // TODO: DTLS contains a hello_cookie in this location
94
   //       Currently we don't support DTLS 1.3
95

96
   // cipher suites
97
   const auto ciphersuites_length = reader.get_uint16_t();
888✔
98
   reader.discard_next(ciphersuites_length);
888✔
99

100
   // compression methods
101
   const auto compression_methods_length = reader.get_byte();
888✔
102
   reader.discard_next(compression_methods_length);
888✔
103

104
   // extensions
105
   const auto extensions_length = reader.get_uint16_t();
888✔
106
   const auto extensions_offset = reader.read_so_far();
888✔
107
   while(reader.has_remaining() && reader.read_so_far() - extensions_offset < extensions_length) {
11,243✔
108
      const auto ext_type = static_cast<Extension_Code>(reader.get_uint16_t());
10,622✔
109
      const auto ext_length = reader.get_uint16_t();
10,622✔
110

111
      // skip over all extensions, finding the PSK extension to be truncated
112
      if(ext_type != Extension_Code::PresharedKey) {
10,622✔
113
         reader.discard_next(ext_length);
10,355✔
114
         continue;
10,355✔
115
      }
116

117
      // PSK identities list
118
      const auto identities_length = reader.get_uint16_t();
267✔
119
      reader.discard_next(identities_length);
267✔
120

121
      // check that only the binders are left in the buffer...
122
      const auto binders_length = reader.peek_uint16_t();
267✔
123
      if(binders_length != reader.remaining_bytes() - 2 /* binders_length */) {
267✔
124
         throw TLS_Exception(Alert::IllegalParameter,
×
125
                             "Failed to truncate Client Hello that doesn't end on the PSK binders list");
×
126
      }
127

128
      // the reader now points to the truncation point
129
      break;
130
   }
131

132
   // if no PSK extension was found, this will point to the end of the buffer
133
   return reader.read_so_far();
888✔
134
}
135

136
std::vector<uint8_t> read_hash_state(std::unique_ptr<HashFunction>& hash) {
5,572✔
137
   // Botan does not support finalizing a HashFunction without resetting
138
   // the internal state of the hash. Hence we first copy the internal
139
   // state and then finalize the transient HashFunction.
140
   return hash->copy_state()->final_stdvec();
11,144✔
141
}
142

143
}  // namespace
144

145
void Transcript_Hash_State::update(std::span<const uint8_t> serialized_message_s) {
8,163✔
146
   auto serialized_message = serialized_message_s.data();
8,163✔
147
   auto serialized_message_length = serialized_message_s.size();
8,163✔
148
   if(m_hash != nullptr) {
8,163✔
149
      auto truncation_mark = serialized_message_length;
5,305✔
150

151
      // Check whether we should generate a truncated hash for supporting PSK
152
      // binder calculation or verification. See RFC 8446 4.2.11.2.
153
      if(serialized_message_length > 0 && *serialized_message == static_cast<uint8_t>(Handshake_Type::ClientHello)) {
5,305✔
154
         truncation_mark = find_client_hello_truncation_mark(serialized_message_s);
888✔
155
      }
156

157
      if(truncation_mark < serialized_message_length) {
5,305✔
158
         m_hash->update(serialized_message, truncation_mark);
267✔
159
         m_truncated = read_hash_state(m_hash);
267✔
160
         m_hash->update(serialized_message + truncation_mark, serialized_message_length - truncation_mark);
267✔
161
      } else {
162
         m_truncated.clear();
5,038✔
163
         m_hash->update(serialized_message, serialized_message_length);
5,038✔
164
      }
165

166
      m_previous = std::exchange(m_current, read_hash_state(m_hash));
5,305✔
167
   } else {
168
      m_unprocessed_transcript.push_back(
5,716✔
169
         std::vector(serialized_message, serialized_message + serialized_message_length));
5,716✔
170
   }
171
}
8,163✔
172

173
const Transcript_Hash& Transcript_Hash_State::current() const {
2,943✔
174
   BOTAN_STATE_CHECK(!m_current.empty());
2,943✔
175
   return m_current;
2,930✔
176
}
177

178
const Transcript_Hash& Transcript_Hash_State::previous() const {
1,077✔
179
   BOTAN_STATE_CHECK(!m_previous.empty());
1,077✔
180
   return m_previous;
1,073✔
181
}
182

183
const Transcript_Hash& Transcript_Hash_State::truncated() const {
188✔
184
   BOTAN_STATE_CHECK(!m_truncated.empty());
188✔
185
   return m_truncated;
187✔
186
}
187

188
void Transcript_Hash_State::set_algorithm(std::string_view algo_spec) {
1,009✔
189
   BOTAN_STATE_CHECK(m_hash == nullptr || m_hash->name() == algo_spec);
1,139✔
190
   if(m_hash != nullptr)
1,007✔
191
      return;
192

193
   m_hash = HashFunction::create_or_throw(algo_spec);
944✔
194
   for(const auto& msg : m_unprocessed_transcript) {
2,153✔
195
      update(msg);
1,209✔
196
   }
197
   m_unprocessed_transcript.clear();
944✔
198
}
199

200
Transcript_Hash_State Transcript_Hash_State::clone() const { return *this; }
102✔
201

202
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc