/src/botan/src/lib/modes/aead/ccm/ccm.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * CCM Mode Encryption |
3 | | * (C) 2013,2018 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/ccm.h> |
10 | | #include <botan/internal/loadstor.h> |
11 | | |
12 | | namespace Botan { |
13 | | |
14 | | // 128-bit cipher is intrinsic to CCM definition |
15 | | static const size_t CCM_BS = 16; |
16 | | |
17 | | /* |
18 | | * CCM_Mode Constructor |
19 | | */ |
20 | | CCM_Mode::CCM_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size, size_t L) : |
21 | | m_tag_size(tag_size), |
22 | | m_L(L), |
23 | | m_cipher(std::move(cipher)) |
24 | 72 | { |
25 | 72 | if(m_cipher->block_size() != CCM_BS) |
26 | 0 | throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode"); |
27 | | |
28 | 72 | if(L < 2 || L > 8) |
29 | 0 | throw Invalid_Argument("Invalid CCM L value " + std::to_string(L)); |
30 | | |
31 | 72 | if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0) |
32 | 0 | throw Invalid_Argument("invalid CCM tag length " + std::to_string(tag_size)); |
33 | 72 | } |
34 | | |
35 | | void CCM_Mode::clear() |
36 | 0 | { |
37 | 0 | m_cipher->clear(); |
38 | 0 | reset(); |
39 | 0 | } |
40 | | |
41 | | void CCM_Mode::reset() |
42 | 31 | { |
43 | 31 | m_nonce.clear(); |
44 | 31 | m_msg_buf.clear(); |
45 | 31 | m_ad_buf.clear(); |
46 | 31 | } |
47 | | |
48 | | std::string CCM_Mode::name() const |
49 | 0 | { |
50 | 0 | return (m_cipher->name() + "/CCM(" + std::to_string(tag_size()) + "," + std::to_string(L())) + ")"; |
51 | 0 | } |
52 | | |
53 | | bool CCM_Mode::valid_nonce_length(size_t n) const |
54 | 73 | { |
55 | 73 | return (n == (15-L())); |
56 | 73 | } |
57 | | |
58 | | size_t CCM_Mode::default_nonce_length() const |
59 | 0 | { |
60 | 0 | return (15-L()); |
61 | 0 | } |
62 | | |
63 | | size_t CCM_Mode::update_granularity() const |
64 | 0 | { |
65 | 0 | return 1; |
66 | 0 | } |
67 | | |
68 | | size_t CCM_Mode::ideal_granularity() const |
69 | 0 | { |
70 | | // Completely arbitrary |
71 | 0 | return m_cipher->parallel_bytes(); |
72 | 0 | } |
73 | | |
74 | | bool CCM_Mode::requires_entire_message() const |
75 | 0 | { |
76 | 0 | return true; |
77 | 0 | } |
78 | | |
79 | | Key_Length_Specification CCM_Mode::key_spec() const |
80 | 72 | { |
81 | 72 | return m_cipher->key_spec(); |
82 | 72 | } |
83 | | |
84 | | void CCM_Mode::key_schedule(const uint8_t key[], size_t length) |
85 | 72 | { |
86 | 72 | m_cipher->set_key(key, length); |
87 | 72 | } |
88 | | |
89 | | void CCM_Mode::set_associated_data(const uint8_t ad[], size_t length) |
90 | 73 | { |
91 | 73 | m_ad_buf.clear(); |
92 | | |
93 | 73 | if(length) |
94 | 73 | { |
95 | | // FIXME: support larger AD using length encoding rules |
96 | 73 | BOTAN_ARG_CHECK(length < (0xFFFF - 0xFF), "Supported CCM AD length"); |
97 | | |
98 | 73 | m_ad_buf.push_back(get_byte<0>(static_cast<uint16_t>(length))); |
99 | 73 | m_ad_buf.push_back(get_byte<1>(static_cast<uint16_t>(length))); |
100 | 73 | m_ad_buf += std::make_pair(ad, length); |
101 | 146 | while(m_ad_buf.size() % CCM_BS) |
102 | 73 | m_ad_buf.push_back(0); // pad with zeros to full block size |
103 | 73 | } |
104 | 73 | } |
105 | | |
106 | | void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) |
107 | 73 | { |
108 | 73 | if(!valid_nonce_length(nonce_len)) |
109 | 0 | throw Invalid_IV_Length(name(), nonce_len); |
110 | | |
111 | 73 | m_nonce.assign(nonce, nonce + nonce_len); |
112 | 73 | m_msg_buf.clear(); |
113 | 73 | } |
114 | | |
115 | | size_t CCM_Mode::process(uint8_t buf[], size_t sz) |
116 | 0 | { |
117 | 0 | BOTAN_STATE_CHECK(!m_nonce.empty()); |
118 | 0 | m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz); |
119 | 0 | return 0; // no output until finished |
120 | 0 | } |
121 | | |
122 | | void CCM_Mode::encode_length(uint64_t len, uint8_t out[]) |
123 | 73 | { |
124 | 73 | const size_t len_bytes = L(); |
125 | | |
126 | 73 | BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8); |
127 | | |
128 | 292 | for(size_t i = 0; i != len_bytes; ++i) |
129 | 219 | out[len_bytes-1-i] = get_byte_var(sizeof(uint64_t)-1-i, len); |
130 | | |
131 | 73 | if(len_bytes < 8 && (len >> (len_bytes*8)) > 0) |
132 | 0 | throw Encoding_Error("CCM message length too long to encode in L field"); |
133 | 73 | } |
134 | | |
135 | | void CCM_Mode::inc(secure_vector<uint8_t>& C) |
136 | 1.86k | { |
137 | 1.86k | for(size_t i = 0; i != C.size(); ++i) |
138 | 1.86k | if(++C[C.size()-i-1]) |
139 | 1.86k | break; |
140 | 1.86k | } |
141 | | |
142 | | secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz) |
143 | 73 | { |
144 | 73 | if(m_nonce.size() != 15-L()) |
145 | 0 | throw Invalid_State("CCM mode must set nonce"); |
146 | 73 | secure_vector<uint8_t> B0(CCM_BS); |
147 | | |
148 | 73 | const uint8_t b_flags = |
149 | 73 | static_cast<uint8_t>((!m_ad_buf.empty() ? 64 : 0) + (((tag_size()/2)-1) << 3) + (L()-1)); |
150 | | |
151 | 73 | B0[0] = b_flags; |
152 | 73 | copy_mem(&B0[1], m_nonce.data(), m_nonce.size()); |
153 | 73 | encode_length(sz, &B0[m_nonce.size()+1]); |
154 | | |
155 | 73 | return B0; |
156 | 73 | } |
157 | | |
158 | | secure_vector<uint8_t> CCM_Mode::format_c0() |
159 | 73 | { |
160 | 73 | if(m_nonce.size() != 15-L()) |
161 | 0 | throw Invalid_State("CCM mode must set nonce"); |
162 | 73 | secure_vector<uint8_t> C(CCM_BS); |
163 | | |
164 | 73 | const uint8_t a_flags = static_cast<uint8_t>(L() - 1); |
165 | | |
166 | 73 | C[0] = a_flags; |
167 | 73 | copy_mem(&C[1], m_nonce.data(), m_nonce.size()); |
168 | | |
169 | 73 | return C; |
170 | 73 | } |
171 | | |
172 | | void CCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) |
173 | 31 | { |
174 | 31 | BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range"); |
175 | | |
176 | 31 | buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); |
177 | | |
178 | 31 | const size_t sz = buffer.size() - offset; |
179 | 31 | uint8_t* buf = buffer.data() + offset; |
180 | | |
181 | 31 | const secure_vector<uint8_t>& ad = ad_buf(); |
182 | 31 | BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple"); |
183 | | |
184 | 31 | const BlockCipher& E = cipher(); |
185 | | |
186 | 31 | secure_vector<uint8_t> T(CCM_BS); |
187 | 31 | E.encrypt(format_b0(sz), T); |
188 | | |
189 | 62 | for(size_t i = 0; i != ad.size(); i += CCM_BS) |
190 | 31 | { |
191 | 31 | xor_buf(T.data(), &ad[i], CCM_BS); |
192 | 31 | E.encrypt(T); |
193 | 31 | } |
194 | | |
195 | 31 | secure_vector<uint8_t> C = format_c0(); |
196 | 31 | secure_vector<uint8_t> S0(CCM_BS); |
197 | 31 | E.encrypt(C, S0); |
198 | 31 | inc(C); |
199 | | |
200 | 31 | secure_vector<uint8_t> X(CCM_BS); |
201 | | |
202 | 31 | const uint8_t* buf_end = &buf[sz]; |
203 | | |
204 | 62 | while(buf != buf_end) |
205 | 31 | { |
206 | 31 | const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf); |
207 | | |
208 | 31 | xor_buf(T.data(), buf, to_proc); |
209 | 31 | E.encrypt(T); |
210 | | |
211 | 31 | E.encrypt(C, X); |
212 | 31 | xor_buf(buf, X.data(), to_proc); |
213 | 31 | inc(C); |
214 | | |
215 | 31 | buf += to_proc; |
216 | 31 | } |
217 | | |
218 | 31 | T ^= S0; |
219 | | |
220 | 31 | buffer += std::make_pair(T.data(), tag_size()); |
221 | | |
222 | 31 | reset(); |
223 | 31 | } |
224 | | |
225 | | void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) |
226 | 42 | { |
227 | 42 | BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range"); |
228 | | |
229 | 42 | buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); |
230 | | |
231 | 42 | const size_t sz = buffer.size() - offset; |
232 | 42 | uint8_t* buf = buffer.data() + offset; |
233 | | |
234 | 42 | BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag"); |
235 | | |
236 | 42 | const secure_vector<uint8_t>& ad = ad_buf(); |
237 | 42 | BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple"); |
238 | | |
239 | 42 | const BlockCipher& E = cipher(); |
240 | | |
241 | 42 | secure_vector<uint8_t> T(CCM_BS); |
242 | 42 | E.encrypt(format_b0(sz - tag_size()), T); |
243 | | |
244 | 84 | for(size_t i = 0; i != ad.size(); i += CCM_BS) |
245 | 42 | { |
246 | 42 | xor_buf(T.data(), &ad[i], CCM_BS); |
247 | 42 | E.encrypt(T); |
248 | 42 | } |
249 | | |
250 | 42 | secure_vector<uint8_t> C = format_c0(); |
251 | | |
252 | 42 | secure_vector<uint8_t> S0(CCM_BS); |
253 | 42 | E.encrypt(C, S0); |
254 | 42 | inc(C); |
255 | | |
256 | 42 | secure_vector<uint8_t> X(CCM_BS); |
257 | | |
258 | 42 | const uint8_t* buf_end = &buf[sz - tag_size()]; |
259 | | |
260 | 1.80k | while(buf != buf_end) |
261 | 1.75k | { |
262 | 1.75k | const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf); |
263 | | |
264 | 1.75k | E.encrypt(C, X); |
265 | 1.75k | xor_buf(buf, X.data(), to_proc); |
266 | 1.75k | inc(C); |
267 | | |
268 | 1.75k | xor_buf(T.data(), buf, to_proc); |
269 | 1.75k | E.encrypt(T); |
270 | | |
271 | 1.75k | buf += to_proc; |
272 | 1.75k | } |
273 | | |
274 | 42 | T ^= S0; |
275 | | |
276 | 42 | if(!constant_time_compare(T.data(), buf_end, tag_size())) |
277 | 42 | throw Invalid_Authentication_Tag("CCM tag check failed"); |
278 | | |
279 | 0 | buffer.resize(buffer.size() - tag_size()); |
280 | |
|
281 | 0 | reset(); |
282 | 0 | } |
283 | | |
284 | | } |