SE050 Driver
API reference
Loading...
Searching...
No Matches
se050_scp03_crypto.hpp
Go to the documentation of this file.
1
14#pragma once
15
16#include <cstddef>
17#include <cstdint>
18#include <cstring>
19
21
29inline void SecureZero(void* p, std::size_t len) noexcept {
30 if (p == nullptr) {
31 return;
32 }
33 volatile std::uint8_t* v = static_cast<volatile std::uint8_t*>(p);
34 for (std::size_t i = 0; i < len; ++i) {
35 v[i] = 0U;
36 }
37#if defined(__GNUC__) || defined(__clang__)
38 __asm__ __volatile__("" : : "r"(p) : "memory");
39#endif
40}
41
44public:
45 ScopedWipe(void* p, std::size_t len) noexcept : p_(p), len_(len) {}
46 ~ScopedWipe() { SecureZero(p_, len_); }
47 ScopedWipe(const ScopedWipe&) = delete;
48 ScopedWipe& operator=(const ScopedWipe&) = delete;
49
50private:
51 void* p_;
52 std::size_t len_;
53};
54
60[[nodiscard]] inline bool ConstantTimeEqual(const std::uint8_t* a, const std::uint8_t* b,
61 std::size_t len) noexcept {
62 if (a == nullptr || b == nullptr) {
63 return false;
64 }
65 volatile std::uint8_t diff = 0U;
66 for (std::size_t i = 0; i < len; ++i) {
67 diff = static_cast<std::uint8_t>(diff | (a[i] ^ b[i]));
68 }
69 return diff == 0U;
70}
71
72inline constexpr std::size_t kAes128KeyLen = 16U;
73inline constexpr std::size_t kCmacLen = 16U;
74inline constexpr std::size_t kChallengeLen = 8U;
75inline constexpr std::size_t kCryptogramLen = 8U;
76inline constexpr std::size_t kLabelLen = 12U;
77
78inline constexpr std::uint8_t kDeriveCardCryptogram = 0x00U;
79inline constexpr std::uint8_t kDeriveHostCryptogram = 0x01U;
80inline constexpr std::uint8_t kDeriveSenc = 0x04U;
81inline constexpr std::uint8_t kDeriveSmac = 0x06U;
82inline constexpr std::uint8_t kDeriveSrmac = 0x07U;
83inline constexpr std::uint8_t kKdfCounter = 0x01U;
84inline constexpr std::uint16_t kLen128Bit = 0x0080U;
85inline constexpr std::uint16_t kLen64Bit = 0x0040U;
86
87inline constexpr std::uint8_t kSbox[256] = {
88 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
89 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
90 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
91 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
92 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
93 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
94 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
95 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
96 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
97 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
98 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
99 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
100 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
101 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
102 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
103 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
104 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
105 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
106 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
107 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
108 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
109 0xb0, 0x54, 0xbb, 0x16};
110
111inline constexpr std::uint8_t kInvSbox[256] = {
112 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
113 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
114 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
115 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
116 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
117 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
118 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
119 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
120 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
121 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
122 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
123 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
124 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
125 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
126 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
127 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
128 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
129 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
130 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
131 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
132 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
133 0x55, 0x21, 0x0c, 0x7d};
134
135inline constexpr std::uint8_t kRcon[10] = {0x01, 0x02, 0x04, 0x08, 0x10,
136 0x20, 0x40, 0x80, 0x1b, 0x36};
137
138inline void AddRoundKey(std::uint8_t s[16], const std::uint8_t* rk) {
139 for (int i = 0; i < 16; ++i) {
140 s[i] = static_cast<std::uint8_t>(s[i] ^ rk[i]);
141 }
142}
143
144inline void SubBytes(std::uint8_t s[16]) {
145 for (int i = 0; i < 16; ++i) {
146 s[i] = kSbox[s[i]];
147 }
148}
149
150inline void InvSubBytes(std::uint8_t s[16]) {
151 for (int i = 0; i < 16; ++i) {
152 s[i] = kInvSbox[s[i]];
153 }
154}
155
156inline void ShiftRows(std::uint8_t s[16]) {
157 std::uint8_t t = s[1];
158 s[1] = s[5];
159 s[5] = s[9];
160 s[9] = s[13];
161 s[13] = t;
162 t = s[2];
163 s[2] = s[10];
164 s[10] = t;
165 t = s[6];
166 s[6] = s[14];
167 s[14] = t;
168 t = s[3];
169 s[3] = s[15];
170 s[15] = s[11];
171 s[11] = s[7];
172 s[7] = t;
173}
174
175inline void InvShiftRows(std::uint8_t s[16]) {
176 std::uint8_t t = s[13];
177 s[13] = s[9];
178 s[9] = s[5];
179 s[5] = s[1];
180 s[1] = t;
181 t = s[2];
182 s[2] = s[10];
183 s[10] = t;
184 t = s[6];
185 s[6] = s[14];
186 s[14] = t;
187 t = s[3];
188 s[3] = s[7];
189 s[7] = s[11];
190 s[11] = s[15];
191 s[15] = t;
192}
193
194inline std::uint8_t Xtime(std::uint8_t x) {
195 return static_cast<std::uint8_t>((x << 1) ^ ((x & 0x80u) ? 0x1bu : 0u));
196}
197
198inline std::uint8_t Gmul(std::uint8_t a, std::uint8_t b) {
199 std::uint8_t p = 0;
200 for (int i = 0; i < 8; ++i) {
201 if ((b & 1U) != 0U) {
202 p = static_cast<std::uint8_t>(p ^ a);
203 }
204 const std::uint8_t hi = static_cast<std::uint8_t>(a & 0x80U);
205 a = static_cast<std::uint8_t>(a << 1);
206 if (hi != 0U) {
207 a = static_cast<std::uint8_t>(a ^ 0x1bU);
208 }
209 b = static_cast<std::uint8_t>(b >> 1);
210 }
211 return p;
212}
213
214inline void MixColumns(std::uint8_t s[16]) {
215 for (int c = 0; c < 4; ++c) {
216 std::uint8_t* col = s + 4 * c;
217 const std::uint8_t a0 = col[0], a1 = col[1], a2 = col[2], a3 = col[3];
218 const std::uint8_t t = static_cast<std::uint8_t>(a0 ^ a1 ^ a2 ^ a3);
219 col[0] = static_cast<std::uint8_t>(a0 ^ t ^ Xtime(static_cast<std::uint8_t>(a0 ^ a1)));
220 col[1] = static_cast<std::uint8_t>(a1 ^ t ^ Xtime(static_cast<std::uint8_t>(a1 ^ a2)));
221 col[2] = static_cast<std::uint8_t>(a2 ^ t ^ Xtime(static_cast<std::uint8_t>(a2 ^ a3)));
222 col[3] = static_cast<std::uint8_t>(a3 ^ t ^ Xtime(static_cast<std::uint8_t>(a3 ^ a0)));
223 }
224}
225
226inline void InvMixColumns(std::uint8_t s[16]) {
227 for (int c = 0; c < 4; ++c) {
228 std::uint8_t* col = s + 4 * c;
229 const std::uint8_t a0 = col[0], a1 = col[1], a2 = col[2], a3 = col[3];
230 col[0] = static_cast<std::uint8_t>(Gmul(a0, 0x0e) ^ Gmul(a1, 0x0b) ^
231 Gmul(a2, 0x0d) ^ Gmul(a3, 0x09));
232 col[1] = static_cast<std::uint8_t>(Gmul(a0, 0x09) ^ Gmul(a1, 0x0e) ^
233 Gmul(a2, 0x0b) ^ Gmul(a3, 0x0d));
234 col[2] = static_cast<std::uint8_t>(Gmul(a0, 0x0d) ^ Gmul(a1, 0x09) ^
235 Gmul(a2, 0x0e) ^ Gmul(a3, 0x0b));
236 col[3] = static_cast<std::uint8_t>(Gmul(a0, 0x0b) ^ Gmul(a1, 0x0d) ^
237 Gmul(a2, 0x09) ^ Gmul(a3, 0x0e));
238 }
239}
240
241inline void ExpandKey(const std::uint8_t key[16], std::uint8_t rk[176]) {
242 std::memcpy(rk, key, 16);
243 for (int i = 4; i < 44; ++i) {
244 std::uint8_t tmp[4] = {rk[4 * (i - 1)], rk[4 * (i - 1) + 1],
245 rk[4 * (i - 1) + 2], rk[4 * (i - 1) + 3]};
246 if ((i % 4) == 0) {
247 const std::uint8_t t0 = tmp[0];
248 tmp[0] = static_cast<std::uint8_t>(kSbox[tmp[1]] ^ kRcon[i / 4 - 1]);
249 tmp[1] = kSbox[tmp[2]];
250 tmp[2] = kSbox[tmp[3]];
251 tmp[3] = kSbox[t0];
252 }
253 rk[4 * i] = static_cast<std::uint8_t>(rk[4 * (i - 4)] ^ tmp[0]);
254 rk[4 * i + 1] = static_cast<std::uint8_t>(rk[4 * (i - 4) + 1] ^ tmp[1]);
255 rk[4 * i + 2] = static_cast<std::uint8_t>(rk[4 * (i - 4) + 2] ^ tmp[2]);
256 rk[4 * i + 3] = static_cast<std::uint8_t>(rk[4 * (i - 4) + 3] ^ tmp[3]);
257 }
258}
259
260inline void Aes128EncryptBlock(const std::uint8_t key[kAes128KeyLen],
261 const std::uint8_t in[16],
262 std::uint8_t out[16]) {
263 std::uint8_t rk[176];
264 ExpandKey(key, rk);
265 std::uint8_t s[16];
266 std::memcpy(s, in, 16);
267 AddRoundKey(s, rk);
268 for (int round = 1; round < 10; ++round) {
269 SubBytes(s);
270 ShiftRows(s);
271 MixColumns(s);
272 AddRoundKey(s, rk + 16 * round);
273 }
274 SubBytes(s);
275 ShiftRows(s);
276 AddRoundKey(s, rk + 160);
277 std::memcpy(out, s, 16);
278 SecureZero(rk, sizeof(rk));
279 SecureZero(s, sizeof(s));
280}
281
282inline void Aes128DecryptBlock(const std::uint8_t key[kAes128KeyLen],
283 const std::uint8_t in[16],
284 std::uint8_t out[16]) {
285 std::uint8_t rk[176];
286 ExpandKey(key, rk);
287 std::uint8_t s[16];
288 std::memcpy(s, in, 16);
289 AddRoundKey(s, rk + 160);
290 for (int round = 9; round >= 1; --round) {
291 InvShiftRows(s);
292 InvSubBytes(s);
293 AddRoundKey(s, rk + 16 * round);
294 InvMixColumns(s);
295 }
296 InvShiftRows(s);
297 InvSubBytes(s);
298 AddRoundKey(s, rk);
299 std::memcpy(out, s, 16);
300 SecureZero(rk, sizeof(rk));
301 SecureZero(s, sizeof(s));
302}
303
304inline void XorBlock(std::uint8_t* a, const std::uint8_t* b) {
305 for (int i = 0; i < 16; ++i) {
306 a[i] = static_cast<std::uint8_t>(a[i] ^ b[i]);
307 }
308}
309
310inline void LeftShift1(std::uint8_t in[16]) {
311 std::uint8_t overflow = 0;
312 for (int i = 15; i >= 0; --i) {
313 const std::uint8_t next = static_cast<std::uint8_t>(in[i] >> 7);
314 in[i] = static_cast<std::uint8_t>((in[i] << 1) | overflow);
315 overflow = next;
316 }
317}
318
319inline void GenerateSubkeys(const std::uint8_t key[16], std::uint8_t k1[16],
320 std::uint8_t k2[16]) {
321 std::uint8_t L[16]{};
322 std::uint8_t z[16]{};
323 Aes128EncryptBlock(key, z, L);
324 const std::uint8_t msb = static_cast<std::uint8_t>(L[0] >> 7);
325 std::memcpy(k1, L, 16);
326 LeftShift1(k1);
327 if (msb) {
328 k1[15] = static_cast<std::uint8_t>(k1[15] ^ 0x87u);
329 }
330 const std::uint8_t msb1 = static_cast<std::uint8_t>(k1[0] >> 7);
331 std::memcpy(k2, k1, 16);
332 LeftShift1(k2);
333 if (msb1) {
334 k2[15] = static_cast<std::uint8_t>(k2[15] ^ 0x87u);
335 }
336 SecureZero(L, sizeof(L));
337}
338
339inline void Aes128Cmac(const std::uint8_t key[kAes128KeyLen],
340 const std::uint8_t* msg, std::size_t msg_len,
341 std::uint8_t out[kCmacLen]) {
342 std::uint8_t k1[16], k2[16];
343 GenerateSubkeys(key, k1, k2);
344 const std::size_t n = (msg_len == 0) ? 1 : ((msg_len + 15) / 16);
345 const bool complete = (msg_len != 0) && ((msg_len % 16) == 0);
346 std::uint8_t x[16]{};
347 std::uint8_t block[16]{};
348 for (std::size_t i = 1; i < n; ++i) {
349 std::memcpy(block, msg + 16 * (i - 1), 16);
350 XorBlock(block, x);
351 Aes128EncryptBlock(key, block, x);
352 }
353 std::memset(block, 0, 16);
354 const std::size_t last_off = (n - 1) * 16;
355 const std::size_t last_len = (msg_len == 0) ? 0 : (msg_len - last_off);
356 if (complete) {
357 std::memcpy(block, msg + last_off, 16);
358 XorBlock(block, k1);
359 } else {
360 if (msg != nullptr && last_len != 0) {
361 std::memcpy(block, msg + last_off, last_len);
362 }
363 block[last_len] = 0x80;
364 XorBlock(block, k2);
365 }
366 XorBlock(block, x);
367 Aes128EncryptBlock(key, block, out);
368 SecureZero(k1, sizeof(k1));
369 SecureZero(k2, sizeof(k2));
370 SecureZero(x, sizeof(x));
371 SecureZero(block, sizeof(block));
372}
373
375inline bool FillDerivationData(std::uint8_t constant, std::uint16_t bit_len,
376 std::uint8_t counter, const std::uint8_t* context,
377 std::uint16_t context_len, std::uint8_t* out,
378 std::size_t out_cap, std::size_t* out_len) {
379 if (out == nullptr || out_len == nullptr) {
380 return false;
381 }
382 const std::size_t need = kLabelLen + 4U + context_len;
383 if (need > out_cap) {
384 return false;
385 }
386 std::memset(out, 0, kLabelLen - 1U);
387 out[kLabelLen - 1U] = constant;
388 out[kLabelLen] = 0x00U;
389 out[kLabelLen + 1U] = static_cast<std::uint8_t>((bit_len >> 8) & 0xFFU);
390 out[kLabelLen + 2U] = static_cast<std::uint8_t>(bit_len & 0xFFU);
391 out[kLabelLen + 3U] = counter;
392 if (context_len != 0U && context != nullptr) {
393 std::memcpy(out + kLabelLen + 4U, context, context_len);
394 }
395 *out_len = need;
396 return true;
397}
398
399inline bool DeriveSessionKey(const std::uint8_t static_key[kAes128KeyLen],
400 std::uint8_t constant,
401 const std::uint8_t host_challenge[kChallengeLen],
402 const std::uint8_t card_challenge[kChallengeLen],
403 std::uint8_t out_key[kAes128KeyLen]) {
404 if (static_key == nullptr || host_challenge == nullptr ||
405 card_challenge == nullptr || out_key == nullptr) {
406 return false;
407 }
408 std::uint8_t context[16];
409 std::memcpy(context, host_challenge, kChallengeLen);
410 std::memcpy(context + kChallengeLen, card_challenge, kChallengeLen);
411 std::uint8_t dd[32];
412 std::size_t dd_len = 0;
413 if (!FillDerivationData(constant, kLen128Bit, kKdfCounter, context,
414 sizeof(context), dd, sizeof(dd), &dd_len)) {
415 return false;
416 }
417 Aes128Cmac(static_key, dd, dd_len, out_key);
418 return true;
419}
420
421inline bool ComputeCryptogram(const std::uint8_t session_mac_key[kAes128KeyLen],
422 std::uint8_t constant,
423 const std::uint8_t host_challenge[kChallengeLen],
424 const std::uint8_t card_challenge[kChallengeLen],
425 std::uint8_t out8[kCryptogramLen]) {
426 if (session_mac_key == nullptr || host_challenge == nullptr ||
427 card_challenge == nullptr || out8 == nullptr) {
428 return false;
429 }
430 std::uint8_t context[16];
431 std::memcpy(context, host_challenge, kChallengeLen);
432 std::memcpy(context + kChallengeLen, card_challenge, kChallengeLen);
433 std::uint8_t dd[32];
434 std::size_t dd_len = 0;
435 if (!FillDerivationData(constant, kLen64Bit, kKdfCounter, context,
436 sizeof(context), dd, sizeof(dd), &dd_len)) {
437 return false;
438 }
439 std::uint8_t full[kCmacLen];
440 Aes128Cmac(session_mac_key, dd, dd_len, full);
441 std::memcpy(out8, full, kCryptogramLen);
442 SecureZero(full, sizeof(full));
443 return true;
444}
445
446inline bool Aes128CbcEncrypt(const std::uint8_t key[kAes128KeyLen],
447 const std::uint8_t iv[16], const std::uint8_t* src,
448 std::uint8_t* dst, std::size_t len) {
449 if (key == nullptr || iv == nullptr || src == nullptr || dst == nullptr ||
450 (len % 16U) != 0U) {
451 return false;
452 }
453 std::uint8_t chain[16];
454 std::memcpy(chain, iv, 16);
455 for (std::size_t off = 0; off < len; off += 16U) {
456 std::uint8_t block[16];
457 std::memcpy(block, src + off, 16);
458 XorBlock(block, chain);
459 Aes128EncryptBlock(key, block, dst + off);
460 std::memcpy(chain, dst + off, 16);
461 SecureZero(block, sizeof(block));
462 }
463 return true;
464}
465
466inline bool Aes128CbcDecrypt(const std::uint8_t key[kAes128KeyLen],
467 const std::uint8_t iv[16], const std::uint8_t* src,
468 std::uint8_t* dst, std::size_t len) {
469 if (key == nullptr || iv == nullptr || src == nullptr || dst == nullptr ||
470 (len % 16U) != 0U) {
471 return false;
472 }
473 std::uint8_t chain[16];
474 std::memcpy(chain, iv, 16);
475 for (std::size_t off = 0; off < len; off += 16U) {
476 std::uint8_t cipher[16];
477 std::memcpy(cipher, src + off, 16);
478 std::uint8_t plain[16];
479 Aes128DecryptBlock(key, cipher, plain);
480 XorBlock(plain, chain);
481 std::memcpy(dst + off, plain, 16);
482 std::memcpy(chain, cipher, 16);
483 SecureZero(plain, sizeof(plain));
484 }
485 return true;
486}
487
488inline bool IsoPad80(std::uint8_t* buf, std::size_t* len, std::size_t cap) {
489 if (buf == nullptr || len == nullptr) {
490 return false;
491 }
492 if (*len >= cap) {
493 return false;
494 }
495 buf[(*len)++] = 0x80U;
496 while ((*len % 16U) != 0U) {
497 if (*len >= cap) {
498 return false;
499 }
500 buf[(*len)++] = 0x00U;
501 }
502 return true;
503}
504
505inline bool StaticKeysUsable(const std::uint8_t* enc, std::size_t enc_len,
506 const std::uint8_t* mac, std::size_t mac_len) {
507 return enc != nullptr && mac != nullptr && enc_len == kAes128KeyLen &&
508 mac_len == kAes128KeyLen;
509}
510
512struct Cmac {
513 std::uint8_t key[kAes128KeyLen]{};
514 std::uint8_t x[16]{};
515 std::uint8_t buf[16]{};
516 std::size_t buf_len{0};
517 std::size_t total{0};
518};
519
520inline void CmacStart(Cmac& c, const std::uint8_t key[kAes128KeyLen]) {
521 std::memcpy(c.key, key, kAes128KeyLen);
522 std::memset(c.x, 0, 16);
523 std::memset(c.buf, 0, 16);
524 c.buf_len = 0;
525 c.total = 0;
526}
527
528inline void CmacUpdate(Cmac& c, const std::uint8_t* data, std::size_t len) {
529 if (data == nullptr && len != 0U) {
530 return;
531 }
532 while (len > 0U) {
533 if (c.buf_len < 16U) {
534 const std::size_t take = (16U - c.buf_len) < len ? (16U - c.buf_len) : len;
535 std::memcpy(c.buf + c.buf_len, data, take);
536 c.buf_len += take;
537 c.total += take;
538 data += take;
539 len -= take;
540 }
541 if (c.buf_len == 16U && len > 0U) {
542 XorBlock(c.buf, c.x);
543 Aes128EncryptBlock(c.key, c.buf, c.x);
544 c.buf_len = 0;
545 }
546 }
547}
548
549inline void CmacFinish(Cmac& c, std::uint8_t out[kCmacLen]) {
550 std::uint8_t k1[16], k2[16];
551 GenerateSubkeys(c.key, k1, k2);
552 std::uint8_t block[16]{};
553 if (c.total == 0U) {
554 block[0] = 0x80U;
555 XorBlock(block, k2);
556 } else if (c.buf_len == 16U) {
557 std::memcpy(block, c.buf, 16);
558 XorBlock(block, k1);
559 } else {
560 std::memcpy(block, c.buf, c.buf_len);
561 block[c.buf_len] = 0x80U;
562 XorBlock(block, k2);
563 }
564 XorBlock(block, c.x);
565 Aes128EncryptBlock(c.key, block, out);
566 SecureZero(k1, sizeof(k1));
567 SecureZero(k2, sizeof(k2));
568 SecureZero(block, sizeof(block));
569 SecureZero(&c, sizeof(c)); /* the context holds a copy of the key */
570}
571
573inline void CommandIcv(const std::uint8_t senc[kAes128KeyLen],
574 const std::uint8_t counter[16], std::uint8_t icv[16]) {
575 Aes128EncryptBlock(senc, counter, icv);
576}
577
579inline void ResponseIcv(const std::uint8_t senc[kAes128KeyLen],
580 const std::uint8_t counter[16], std::uint8_t icv[16]) {
581 std::uint8_t padded[16];
582 std::memcpy(padded, counter, 16);
583 padded[0] = 0x80U;
584 Aes128EncryptBlock(senc, padded, icv);
585}
586
587inline void IncCommandCounter(std::uint8_t counter[16]) {
588 for (int i = 15; i > 0; --i) {
589 if (counter[i] < 0xFFU) {
590 ++counter[i];
591 return;
592 }
593 counter[i] = 0;
594 }
595}
596
597inline bool IsoUnpad80(const std::uint8_t* padded, std::size_t padded_len,
598 std::size_t* plain_len) {
599 if (padded == nullptr || plain_len == nullptr || padded_len < 16U ||
600 (padded_len % 16U) != 0U) {
601 return false;
602 }
603 std::size_t i = padded_len;
604 while (i > (padded_len - 16U) && i > 0U) {
605 if (padded[i - 1U] == 0x00U) {
606 --i;
607 continue;
608 }
609 if (padded[i - 1U] == 0x80U) {
610 *plain_len = i - 1U;
611 return true;
612 }
613 return false;
614 }
615 return false;
616}
617
618} // namespace se050::scp03::crypto
Wipes a buffer with SecureZero when the scope ends, on every return path.
Definition se050_scp03_crypto.hpp:43
ScopedWipe(void *p, std::size_t len) noexcept
Definition se050_scp03_crypto.hpp:45
ScopedWipe & operator=(const ScopedWipe &)=delete
ScopedWipe(const ScopedWipe &)=delete
~ScopedWipe()
Definition se050_scp03_crypto.hpp:46
Definition se050_scp03_crypto.hpp:20
void AddRoundKey(std::uint8_t s[16], const std::uint8_t *rk)
Definition se050_scp03_crypto.hpp:138
void ShiftRows(std::uint8_t s[16])
Definition se050_scp03_crypto.hpp:156
constexpr std::size_t kChallengeLen
Definition se050_scp03_crypto.hpp:74
void InvMixColumns(std::uint8_t s[16])
Definition se050_scp03_crypto.hpp:226
constexpr std::uint8_t kDeriveSenc
Definition se050_scp03_crypto.hpp:80
void IncCommandCounter(std::uint8_t counter[16])
Definition se050_scp03_crypto.hpp:587
bool Aes128CbcEncrypt(const std::uint8_t key[kAes128KeyLen], const std::uint8_t iv[16], const std::uint8_t *src, std::uint8_t *dst, std::size_t len)
Definition se050_scp03_crypto.hpp:446
constexpr std::uint8_t kRcon[10]
Definition se050_scp03_crypto.hpp:135
bool StaticKeysUsable(const std::uint8_t *enc, std::size_t enc_len, const std::uint8_t *mac, std::size_t mac_len)
Definition se050_scp03_crypto.hpp:505
constexpr std::uint8_t kInvSbox[256]
Definition se050_scp03_crypto.hpp:111
constexpr std::uint16_t kLen128Bit
Definition se050_scp03_crypto.hpp:84
bool IsoPad80(std::uint8_t *buf, std::size_t *len, std::size_t cap)
Definition se050_scp03_crypto.hpp:488
void MixColumns(std::uint8_t s[16])
Definition se050_scp03_crypto.hpp:214
constexpr std::uint8_t kDeriveHostCryptogram
Definition se050_scp03_crypto.hpp:79
void CmacUpdate(Cmac &c, const std::uint8_t *data, std::size_t len)
Definition se050_scp03_crypto.hpp:528
constexpr std::uint8_t kDeriveCardCryptogram
Definition se050_scp03_crypto.hpp:78
void LeftShift1(std::uint8_t in[16])
Definition se050_scp03_crypto.hpp:310
void GenerateSubkeys(const std::uint8_t key[16], std::uint8_t k1[16], std::uint8_t k2[16])
Definition se050_scp03_crypto.hpp:319
constexpr std::size_t kCryptogramLen
Definition se050_scp03_crypto.hpp:75
constexpr std::size_t kAes128KeyLen
Definition se050_scp03_crypto.hpp:72
bool FillDerivationData(std::uint8_t constant, std::uint16_t bit_len, std::uint8_t counter, const std::uint8_t *context, std::uint16_t context_len, std::uint8_t *out, std::size_t out_cap, std::size_t *out_len)
GP Amendment D derivation input (label || 00 || L || i || context).
Definition se050_scp03_crypto.hpp:375
constexpr std::uint8_t kDeriveSrmac
Definition se050_scp03_crypto.hpp:82
void Aes128DecryptBlock(const std::uint8_t key[kAes128KeyLen], const std::uint8_t in[16], std::uint8_t out[16])
Definition se050_scp03_crypto.hpp:282
constexpr std::uint8_t kSbox[256]
Definition se050_scp03_crypto.hpp:87
void SecureZero(void *p, std::size_t len) noexcept
Overwrite len bytes at p with zeros in a way the compiler cannot drop.
Definition se050_scp03_crypto.hpp:29
constexpr std::uint8_t kKdfCounter
Definition se050_scp03_crypto.hpp:83
constexpr std::uint8_t kDeriveSmac
Definition se050_scp03_crypto.hpp:81
void ExpandKey(const std::uint8_t key[16], std::uint8_t rk[176])
Definition se050_scp03_crypto.hpp:241
void InvSubBytes(std::uint8_t s[16])
Definition se050_scp03_crypto.hpp:150
constexpr std::uint16_t kLen64Bit
Definition se050_scp03_crypto.hpp:85
bool IsoUnpad80(const std::uint8_t *padded, std::size_t padded_len, std::size_t *plain_len)
Definition se050_scp03_crypto.hpp:597
void CommandIcv(const std::uint8_t senc[kAes128KeyLen], const std::uint8_t counter[16], std::uint8_t icv[16])
Command ICV = AES-ECB(SENC, counter). CBC with IV=0 of one block.
Definition se050_scp03_crypto.hpp:573
bool ComputeCryptogram(const std::uint8_t session_mac_key[kAes128KeyLen], std::uint8_t constant, const std::uint8_t host_challenge[kChallengeLen], const std::uint8_t card_challenge[kChallengeLen], std::uint8_t out8[kCryptogramLen])
Definition se050_scp03_crypto.hpp:421
constexpr std::size_t kLabelLen
Definition se050_scp03_crypto.hpp:76
void Aes128EncryptBlock(const std::uint8_t key[kAes128KeyLen], const std::uint8_t in[16], std::uint8_t out[16])
Definition se050_scp03_crypto.hpp:260
void CmacStart(Cmac &c, const std::uint8_t key[kAes128KeyLen])
Definition se050_scp03_crypto.hpp:520
bool Aes128CbcDecrypt(const std::uint8_t key[kAes128KeyLen], const std::uint8_t iv[16], const std::uint8_t *src, std::uint8_t *dst, std::size_t len)
Definition se050_scp03_crypto.hpp:466
constexpr std::size_t kCmacLen
Definition se050_scp03_crypto.hpp:73
std::uint8_t Gmul(std::uint8_t a, std::uint8_t b)
Definition se050_scp03_crypto.hpp:198
void CmacFinish(Cmac &c, std::uint8_t out[kCmacLen])
Definition se050_scp03_crypto.hpp:549
void ResponseIcv(const std::uint8_t senc[kAes128KeyLen], const std::uint8_t counter[16], std::uint8_t icv[16])
Response ICV: counter with MSB 0x80, then AES-ECB(SENC, ·).
Definition se050_scp03_crypto.hpp:579
bool ConstantTimeEqual(const std::uint8_t *a, const std::uint8_t *b, std::size_t len) noexcept
Compare two byte strings in time that depends only on len, not on where they differ.
Definition se050_scp03_crypto.hpp:60
void Aes128Cmac(const std::uint8_t key[kAes128KeyLen], const std::uint8_t *msg, std::size_t msg_len, std::uint8_t out[kCmacLen])
Definition se050_scp03_crypto.hpp:339
std::uint8_t Xtime(std::uint8_t x)
Definition se050_scp03_crypto.hpp:194
bool DeriveSessionKey(const std::uint8_t static_key[kAes128KeyLen], std::uint8_t constant, const std::uint8_t host_challenge[kChallengeLen], const std::uint8_t card_challenge[kChallengeLen], std::uint8_t out_key[kAes128KeyLen])
Definition se050_scp03_crypto.hpp:399
void InvShiftRows(std::uint8_t s[16])
Definition se050_scp03_crypto.hpp:175
void SubBytes(std::uint8_t s[16])
Definition se050_scp03_crypto.hpp:144
void XorBlock(std::uint8_t *a, const std::uint8_t *b)
Definition se050_scp03_crypto.hpp:304
Streaming AES-128-CMAC (RFC 4493) — no concatenated MAC buffer.
Definition se050_scp03_crypto.hpp:512
std::uint8_t x[16]
Definition se050_scp03_crypto.hpp:514
std::uint8_t key[kAes128KeyLen]
Definition se050_scp03_crypto.hpp:513
std::uint8_t buf[16]
Definition se050_scp03_crypto.hpp:515
std::size_t total
Definition se050_scp03_crypto.hpp:517
std::size_t buf_len
Definition se050_scp03_crypto.hpp:516