/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(BlockCipher* cipher, size_t tag_size, size_t L) : |
21 | | m_tag_size(tag_size), |
22 | | m_L(L), |
23 | | m_cipher(cipher) |
24 | 261 | { |
25 | 261 | if(m_cipher->block_size() != CCM_BS) |
26 | 0 | throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode"); |
27 | | |
28 | 261 | if(L < 2 || L > 8) |
29 | 0 | throw Invalid_Argument("Invalid CCM L value " + std::to_string(L)); |
30 | | |
31 | 261 | 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 | 261 | } |
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 | 221 | { |
43 | 221 | m_nonce.clear(); |
44 | 221 | m_msg_buf.clear(); |
45 | 221 | m_ad_buf.clear(); |
46 | 221 | } |
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 | 312 | { |
55 | 312 | return (n == (15-L())); |
56 | 312 | } |
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 | | /* |
66 | | This value does not particularly matter as regardless CCM_Mode::update |
67 | | buffers all input, so in theory this could be 1. However as for instance |
68 | | Transform_Filter creates update_granularity() uint8_t buffers, use a |
69 | | somewhat large size to avoid bouncing on a tiny buffer. |
70 | | */ |
71 | 0 | return m_cipher->parallel_bytes(); |
72 | 0 | } |
73 | | |
74 | | Key_Length_Specification CCM_Mode::key_spec() const |
75 | 261 | { |
76 | 261 | return m_cipher->key_spec(); |
77 | 261 | } |
78 | | |
79 | | void CCM_Mode::key_schedule(const uint8_t key[], size_t length) |
80 | 261 | { |
81 | 261 | m_cipher->set_key(key, length); |
82 | 261 | } |
83 | | |
84 | | void CCM_Mode::set_associated_data(const uint8_t ad[], size_t length) |
85 | 312 | { |
86 | 312 | m_ad_buf.clear(); |
87 | | |
88 | 312 | if(length) |
89 | 312 | { |
90 | | // FIXME: support larger AD using length encoding rules |
91 | 312 | BOTAN_ARG_CHECK(length < (0xFFFF - 0xFF), "Supported CCM AD length"); |
92 | | |
93 | 312 | m_ad_buf.push_back(get_byte(0, static_cast<uint16_t>(length))); |
94 | 312 | m_ad_buf.push_back(get_byte(1, static_cast<uint16_t>(length))); |
95 | 312 | m_ad_buf += std::make_pair(ad, length); |
96 | 624 | while(m_ad_buf.size() % CCM_BS) |
97 | 312 | m_ad_buf.push_back(0); // pad with zeros to full block size |
98 | 312 | } |
99 | 312 | } |
100 | | |
101 | | void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) |
102 | 312 | { |
103 | 312 | if(!valid_nonce_length(nonce_len)) |
104 | 0 | throw Invalid_IV_Length(name(), nonce_len); |
105 | | |
106 | 312 | m_nonce.assign(nonce, nonce + nonce_len); |
107 | 312 | m_msg_buf.clear(); |
108 | 312 | } |
109 | | |
110 | | size_t CCM_Mode::process(uint8_t buf[], size_t sz) |
111 | 0 | { |
112 | 0 | BOTAN_STATE_CHECK(m_nonce.size() > 0); |
113 | 0 | m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz); |
114 | 0 | return 0; // no output until finished |
115 | 0 | } |
116 | | |
117 | | void CCM_Mode::encode_length(uint64_t len, uint8_t out[]) |
118 | 312 | { |
119 | 312 | const size_t len_bytes = L(); |
120 | | |
121 | 312 | BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8); |
122 | | |
123 | 1.24k | for(size_t i = 0; i != len_bytes; ++i) |
124 | 936 | out[len_bytes-1-i] = get_byte(sizeof(uint64_t)-1-i, len); |
125 | | |
126 | 312 | if(len_bytes < 8 && (len >> (len_bytes*8)) > 0) |
127 | 0 | throw Encoding_Error("CCM message length too long to encode in L field"); |
128 | 312 | } |
129 | | |
130 | | void CCM_Mode::inc(secure_vector<uint8_t>& C) |
131 | 5.91k | { |
132 | 5.91k | for(size_t i = 0; i != C.size(); ++i) |
133 | 5.91k | if(++C[C.size()-i-1]) |
134 | 5.91k | break; |
135 | 5.91k | } |
136 | | |
137 | | secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz) |
138 | 312 | { |
139 | 312 | if(m_nonce.size() != 15-L()) |
140 | 0 | throw Invalid_State("CCM mode must set nonce"); |
141 | 312 | secure_vector<uint8_t> B0(CCM_BS); |
142 | | |
143 | 312 | const uint8_t b_flags = |
144 | 312 | static_cast<uint8_t>((m_ad_buf.size() ? 64 : 0) + (((tag_size()/2)-1) << 3) + (L()-1)); |
145 | | |
146 | 312 | B0[0] = b_flags; |
147 | 312 | copy_mem(&B0[1], m_nonce.data(), m_nonce.size()); |
148 | 312 | encode_length(sz, &B0[m_nonce.size()+1]); |
149 | | |
150 | 312 | return B0; |
151 | 312 | } |
152 | | |
153 | | secure_vector<uint8_t> CCM_Mode::format_c0() |
154 | 312 | { |
155 | 312 | if(m_nonce.size() != 15-L()) |
156 | 0 | throw Invalid_State("CCM mode must set nonce"); |
157 | 312 | secure_vector<uint8_t> C(CCM_BS); |
158 | | |
159 | 312 | const uint8_t a_flags = static_cast<uint8_t>(L() - 1); |
160 | | |
161 | 312 | C[0] = a_flags; |
162 | 312 | copy_mem(&C[1], m_nonce.data(), m_nonce.size()); |
163 | | |
164 | 312 | return C; |
165 | 312 | } |
166 | | |
167 | | void CCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) |
168 | 221 | { |
169 | 221 | BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane"); |
170 | | |
171 | 221 | buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); |
172 | | |
173 | 221 | const size_t sz = buffer.size() - offset; |
174 | 221 | uint8_t* buf = buffer.data() + offset; |
175 | | |
176 | 221 | const secure_vector<uint8_t>& ad = ad_buf(); |
177 | 221 | BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple"); |
178 | | |
179 | 221 | const BlockCipher& E = cipher(); |
180 | | |
181 | 221 | secure_vector<uint8_t> T(CCM_BS); |
182 | 221 | E.encrypt(format_b0(sz), T); |
183 | | |
184 | 442 | for(size_t i = 0; i != ad.size(); i += CCM_BS) |
185 | 221 | { |
186 | 221 | xor_buf(T.data(), &ad[i], CCM_BS); |
187 | 221 | E.encrypt(T); |
188 | 221 | } |
189 | | |
190 | 221 | secure_vector<uint8_t> C = format_c0(); |
191 | 221 | secure_vector<uint8_t> S0(CCM_BS); |
192 | 221 | E.encrypt(C, S0); |
193 | 221 | inc(C); |
194 | | |
195 | 221 | secure_vector<uint8_t> X(CCM_BS); |
196 | | |
197 | 221 | const uint8_t* buf_end = &buf[sz]; |
198 | | |
199 | 442 | while(buf != buf_end) |
200 | 221 | { |
201 | 221 | const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf); |
202 | | |
203 | 221 | xor_buf(T.data(), buf, to_proc); |
204 | 221 | E.encrypt(T); |
205 | | |
206 | 221 | E.encrypt(C, X); |
207 | 221 | xor_buf(buf, X.data(), to_proc); |
208 | 221 | inc(C); |
209 | | |
210 | 221 | buf += to_proc; |
211 | 221 | } |
212 | | |
213 | 221 | T ^= S0; |
214 | | |
215 | 221 | buffer += std::make_pair(T.data(), tag_size()); |
216 | | |
217 | 221 | reset(); |
218 | 221 | } |
219 | | |
220 | | void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) |
221 | 91 | { |
222 | 91 | BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane"); |
223 | | |
224 | 91 | buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); |
225 | | |
226 | 91 | const size_t sz = buffer.size() - offset; |
227 | 91 | uint8_t* buf = buffer.data() + offset; |
228 | | |
229 | 91 | BOTAN_ASSERT(sz >= tag_size(), "We have the tag"); |
230 | | |
231 | 91 | const secure_vector<uint8_t>& ad = ad_buf(); |
232 | 91 | BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple"); |
233 | | |
234 | 91 | const BlockCipher& E = cipher(); |
235 | | |
236 | 91 | secure_vector<uint8_t> T(CCM_BS); |
237 | 91 | E.encrypt(format_b0(sz - tag_size()), T); |
238 | | |
239 | 182 | for(size_t i = 0; i != ad.size(); i += CCM_BS) |
240 | 91 | { |
241 | 91 | xor_buf(T.data(), &ad[i], CCM_BS); |
242 | 91 | E.encrypt(T); |
243 | 91 | } |
244 | | |
245 | 91 | secure_vector<uint8_t> C = format_c0(); |
246 | | |
247 | 91 | secure_vector<uint8_t> S0(CCM_BS); |
248 | 91 | E.encrypt(C, S0); |
249 | 91 | inc(C); |
250 | | |
251 | 91 | secure_vector<uint8_t> X(CCM_BS); |
252 | | |
253 | 91 | const uint8_t* buf_end = &buf[sz - tag_size()]; |
254 | | |
255 | 5.47k | while(buf != buf_end) |
256 | 5.38k | { |
257 | 5.38k | const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf); |
258 | | |
259 | 5.38k | E.encrypt(C, X); |
260 | 5.38k | xor_buf(buf, X.data(), to_proc); |
261 | 5.38k | inc(C); |
262 | | |
263 | 5.38k | xor_buf(T.data(), buf, to_proc); |
264 | 5.38k | E.encrypt(T); |
265 | | |
266 | 5.38k | buf += to_proc; |
267 | 5.38k | } |
268 | | |
269 | 91 | T ^= S0; |
270 | | |
271 | 91 | if(!constant_time_compare(T.data(), buf_end, tag_size())) |
272 | 91 | throw Invalid_Authentication_Tag("CCM tag check failed"); |
273 | | |
274 | 0 | buffer.resize(buffer.size() - tag_size()); |
275 | |
|
276 | 0 | reset(); |
277 | 0 | } |
278 | | |
279 | | } |