Coverage Report

Created: 2025-11-16 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/modes/aead/eax/eax.cpp
Line
Count
Source
1
/*
2
* EAX Mode Encryption
3
* (C) 1999-2007 Jack Lloyd
4
* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8
9
#include <botan/internal/eax.h>
10
11
#include <botan/mem_ops.h>
12
#include <botan/internal/cmac.h>
13
#include <botan/internal/ct_utils.h>
14
#include <botan/internal/ctr.h>
15
#include <botan/internal/fmt.h>
16
17
namespace Botan {
18
19
namespace {
20
21
/*
22
* EAX MAC-based PRF
23
*/
24
secure_vector<uint8_t> eax_prf(
25
0
   uint8_t tag, size_t block_size, MessageAuthenticationCode& mac, const uint8_t in[], size_t length) {
26
0
   for(size_t i = 0; i != block_size - 1; ++i) {
27
0
      mac.update(0);
28
0
   }
29
0
   mac.update(tag);
30
0
   mac.update(in, length);
31
0
   return mac.final();
32
0
}
33
34
}  // namespace
35
36
/*
37
* EAX_Mode Constructor
38
*/
39
EAX_Mode::EAX_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size) :
40
0
      m_tag_size(tag_size),
41
0
      m_cipher(std::move(cipher)),
42
0
      m_ctr(std::make_unique<CTR_BE>(m_cipher->new_object())),
43
0
      m_cmac(std::make_unique<CMAC>(m_cipher->new_object())) {
44
0
   if(m_tag_size < 8 || m_tag_size > m_cmac->output_length()) {
45
0
      throw Invalid_Argument(fmt("Tag size {} is not allowed for {}", tag_size, name()));
46
0
   }
47
0
}
48
49
0
void EAX_Mode::clear() {
50
0
   m_cipher->clear();
51
0
   m_ctr->clear();
52
0
   m_cmac->clear();
53
0
   reset();
54
0
}
55
56
0
void EAX_Mode::reset() {
57
0
   m_ad_mac.clear();
58
0
   m_nonce_mac.clear();
59
60
   // Clear out any data added to the CMAC calculation
61
0
   try {
62
0
      m_cmac->final();
63
0
   } catch(Key_Not_Set&) {}
64
0
}
65
66
0
std::string EAX_Mode::name() const {
67
0
   return (m_cipher->name() + "/EAX");
68
0
}
69
70
0
size_t EAX_Mode::update_granularity() const {
71
0
   return 1;
72
0
}
73
74
0
size_t EAX_Mode::ideal_granularity() const {
75
0
   return m_cipher->parallel_bytes();
76
0
}
77
78
0
Key_Length_Specification EAX_Mode::key_spec() const {
79
0
   return m_ctr->key_spec();
80
0
}
81
82
0
bool EAX_Mode::has_keying_material() const {
83
0
   return m_ctr->has_keying_material() && m_cmac->has_keying_material();
84
0
}
85
86
/*
87
* Set the EAX key
88
*/
89
0
void EAX_Mode::key_schedule(std::span<const uint8_t> key) {
90
   /*
91
   * These could share the key schedule, which is one nice part of EAX,
92
   * but it's much easier to ignore that here...
93
   */
94
0
   m_ctr->set_key(key);
95
0
   m_cmac->set_key(key);
96
0
}
97
98
/*
99
* Set the EAX associated data
100
*/
101
0
void EAX_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
102
0
   BOTAN_ARG_CHECK(idx == 0, "EAX: cannot handle non-zero index in set_associated_data_n");
103
0
   if(!m_nonce_mac.empty()) {
104
0
      throw Invalid_State("Cannot set AD for EAX while processing a message");
105
0
   }
106
0
   m_ad_mac = eax_prf(1, block_size(), *m_cmac, ad.data(), ad.size());
107
0
}
108
109
0
void EAX_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
110
0
   if(!valid_nonce_length(nonce_len)) {
111
0
      throw Invalid_IV_Length(name(), nonce_len);
112
0
   }
113
114
0
   m_nonce_mac = eax_prf(0, block_size(), *m_cmac, nonce, nonce_len);
115
116
0
   m_ctr->set_iv(m_nonce_mac.data(), m_nonce_mac.size());
117
118
0
   for(size_t i = 0; i != block_size() - 1; ++i) {
119
0
      m_cmac->update(0);
120
0
   }
121
0
   m_cmac->update(2);
122
0
}
123
124
0
size_t EAX_Encryption::process_msg(uint8_t buf[], size_t sz) {
125
0
   BOTAN_STATE_CHECK(!m_nonce_mac.empty());
126
0
   m_ctr->cipher(buf, buf, sz);
127
0
   m_cmac->update(buf, sz);
128
0
   return sz;
129
0
}
130
131
0
void EAX_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
132
0
   BOTAN_STATE_CHECK(!m_nonce_mac.empty());
133
0
   update(buffer, offset);
134
135
0
   secure_vector<uint8_t> data_mac = m_cmac->final();
136
0
   xor_buf(data_mac, m_nonce_mac, data_mac.size());
137
138
0
   if(m_ad_mac.empty()) {
139
0
      m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0);
140
0
   }
141
142
0
   xor_buf(data_mac, m_ad_mac, data_mac.size());
143
144
0
   buffer += std::make_pair(data_mac.data(), tag_size());
145
146
0
   m_nonce_mac.clear();
147
0
}
148
149
0
size_t EAX_Decryption::process_msg(uint8_t buf[], size_t sz) {
150
0
   BOTAN_STATE_CHECK(!m_nonce_mac.empty());
151
0
   m_cmac->update(buf, sz);
152
0
   m_ctr->cipher(buf, buf, sz);
153
0
   return sz;
154
0
}
155
156
0
void EAX_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
157
0
   BOTAN_STATE_CHECK(!m_nonce_mac.empty());
158
0
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
159
0
   const size_t sz = buffer.size() - offset;
160
0
   uint8_t* buf = buffer.data() + offset;
161
162
0
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
163
164
0
   const size_t remaining = sz - tag_size();
165
166
0
   if(remaining > 0) {
167
0
      m_cmac->update(buf, remaining);
168
0
      m_ctr->cipher(buf, buf, remaining);
169
0
   }
170
171
0
   const uint8_t* included_tag = &buf[remaining];
172
173
0
   secure_vector<uint8_t> mac = m_cmac->final();
174
0
   mac ^= m_nonce_mac;
175
176
0
   if(m_ad_mac.empty()) {
177
0
      m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0);
178
0
   }
179
180
0
   mac ^= m_ad_mac;
181
182
0
   const bool accept_mac = CT::is_equal(mac.data(), included_tag, tag_size()).as_bool();
183
184
0
   buffer.resize(offset + remaining);
185
186
0
   m_nonce_mac.clear();
187
188
0
   if(!accept_mac) {
189
0
      throw Invalid_Authentication_Tag("EAX tag check failed");
190
0
   }
191
0
}
192
193
}  // namespace Botan