SE050 Driver
API reference
Loading...
Searching...
No Matches
se050_scp03.hpp
Go to the documentation of this file.
1
17#pragma once
18
19#include "se050_apdu.hpp"
21#include "se050_t1_session.hpp"
22#include "se050_types.hpp"
23
24#include <cstddef>
25#include <cstdint>
26#include <cstring>
27
28namespace se050::scp03 {
29
30inline constexpr std::uint8_t kClaGp = 0x80U;
31inline constexpr std::uint8_t kClaGpSecure = 0x84U;
32inline constexpr std::uint8_t kInsInitializeUpdate = 0x50U;
33inline constexpr std::uint8_t kInsExternalAuthenticate = 0x82U;
34inline constexpr std::uint8_t kDefaultKeyVersion = 0x0BU;
35inline constexpr std::uint8_t kSecLevelCmacCencRmacRenc = 0x33U;
36inline constexpr std::size_t kInitUpdateRspMin = 10U + 3U + 8U + 8U + 2U;
37inline constexpr std::uint8_t kClaSmBit = 0x04U;
38inline constexpr std::size_t kMacFieldLen = 8U;
40inline constexpr std::size_t kWrapScratchBytes = 320U;
41
43struct StaticKeys {
44 const std::uint8_t* host_static_key_enc{nullptr};
46 const std::uint8_t* host_static_key_cmac{nullptr};
48 const std::uint8_t* host_challenge{nullptr};
49 std::size_t host_challenge_len{0};
51};
52
55 bool open{false};
56 std::uint8_t senc[crypto::kAes128KeyLen]{};
57 std::uint8_t smac[crypto::kAes128KeyLen]{};
59 std::uint8_t mcv[16]{};
60 std::uint8_t counter[16]{};
61};
62
64inline constexpr std::size_t kWrapScratchBytesExtended = kMaxApduCommandBytes + 7U + 16U + kMacFieldLen + 2U;
65
68 const std::uint8_t* data{nullptr};
69 std::size_t data_len{0};
70 bool extended{false};
71 bool has_le{false};
72};
73
78[[nodiscard]] inline Error ParseCommand(const std::uint8_t* capdu, std::size_t len, CommandFields* f) noexcept {
79 if (capdu == nullptr || f == nullptr || len < 4U) {
81 }
82 *f = CommandFields{};
83 if (len == 4U) {
84 return Error::Ok; /* case 1 */
85 }
86 if (len == 5U) {
87 f->has_le = true; /* case 2S */
88 return Error::Ok;
89 }
90 if (capdu[4] != 0x00U) {
91 const std::size_t lc = capdu[4];
92 if (len == 5U + lc || len == 6U + lc) { /* case 3S / 4S */
93 f->data = capdu + 5U;
94 f->data_len = lc;
95 f->has_le = (len == 6U + lc);
96 return Error::Ok;
97 }
98 return Error::Protocol;
99 }
100 f->extended = true;
101 if (len == 7U) {
102 f->has_le = true; /* case 2E: 00 Le_hi Le_lo */
103 return Error::Ok;
104 }
105 const std::size_t lc = (static_cast<std::size_t>(capdu[5]) << 8) | capdu[6];
106 if (lc == 0U) {
107 return Error::Protocol;
108 }
109 if (len == 7U + lc || len == 9U + lc) { /* case 3E / 4E */
110 f->data = capdu + 7U;
111 f->data_len = lc;
112 f->has_le = (len == 9U + lc);
113 return Error::Ok;
114 }
115 return Error::Protocol;
116}
117
132[[nodiscard]] inline Error WrapCommand(ChannelState& st, const std::uint8_t* capdu,
133 std::size_t capdu_len, std::uint8_t* out,
134 std::size_t out_cap, std::size_t* out_len) noexcept {
135 if (out_len == nullptr) {
137 }
138 *out_len = 0;
139 if (!st.open) {
140 return Error::NotSupported;
141 }
142 if (capdu == nullptr || out == nullptr || capdu_len < 4U) {
144 }
145 CommandFields f{};
146 const Error pe = ParseCommand(capdu, capdu_len, &f);
147 if (pe != Error::Ok) {
148 return pe;
149 }
150
151 /* SE050 erratum JOS.2 (errata sheet Rev. 1.0, 3.13): inside platform SCP03 the SE050 does not advance
152 * its encryption counter for a command without C-DATA, and the next command fails. Workaround 3.13.3:
153 * append command data. Every command is therefore padded and encrypted, an empty one as a single
154 * `80 00 .. 00` block, so a case 1 command goes out as case 3 and a case 2 as case 4; the card
155 * decrypts the block back to no data and both counters advance. */
156 const std::size_t enc_len = ((f.data_len / 16U) + 1U) * 16U;
157 const std::size_t lc = enc_len + kMacFieldLen;
158 const bool extended = f.extended || lc > 255U;
159 if (lc > 0xFFFFU) {
161 }
162 const std::size_t hdr = extended ? 7U : 5U;
163 const std::size_t le_len = f.has_le ? (extended ? 2U : 1U) : 0U;
164 if ((hdr + lc + le_len) > out_cap) {
166 }
167
168 out[0] = static_cast<std::uint8_t>(capdu[0] | kClaSmBit);
169 out[1] = capdu[1];
170 out[2] = capdu[2];
171 out[3] = capdu[3];
172 if (extended) {
173 out[4] = 0x00U;
174 out[5] = static_cast<std::uint8_t>((lc >> 8) & 0xFFU);
175 out[6] = static_cast<std::uint8_t>(lc & 0xFFU);
176 } else {
177 out[4] = static_cast<std::uint8_t>(lc);
178 }
179
180 {
181 if (f.data_len > 0U) {
182 std::memmove(out + hdr, f.data, f.data_len);
183 }
184 std::size_t padded = f.data_len;
185 if (!crypto::IsoPad80(out + hdr, &padded, enc_len) || padded != enc_len) {
186 crypto::SecureZero(out, out_cap);
188 }
189 std::uint8_t icv[16]{};
190 crypto::ScopedWipe wipe_icv(icv, sizeof(icv));
191 crypto::CommandIcv(st.senc, st.counter, icv);
192 if (!crypto::Aes128CbcEncrypt(st.senc, icv, out + hdr, out + hdr, enc_len)) {
193 crypto::SecureZero(out, out_cap);
195 }
196 }
197
198 crypto::Cmac cm{};
199 crypto::CmacStart(cm, st.smac);
200 crypto::CmacUpdate(cm, st.mcv, 16U);
201 crypto::CmacUpdate(cm, out, hdr + enc_len);
202 std::uint8_t mac_full[16]{};
203 crypto::ScopedWipe wipe_mac(mac_full, sizeof(mac_full));
204 crypto::CmacFinish(cm, mac_full);
205 std::memcpy(out + hdr + enc_len, mac_full, kMacFieldLen);
206 std::memcpy(st.mcv, mac_full, 16);
207 std::size_t n = hdr + lc;
208 for (std::size_t i = 0; i < le_len; ++i) {
209 out[n++] = 0x00U;
210 }
211 *out_len = n;
212 return Error::Ok;
213}
214
219inline void WipeChannel(ChannelState& st) noexcept { crypto::SecureZero(&st, sizeof(st)); }
220
230[[nodiscard]] inline Error UnwrapResponse(ChannelState& st, const std::uint8_t* rapdu,
231 std::size_t rapdu_len, std::uint8_t* out,
232 std::size_t out_cap, std::size_t* out_len) noexcept {
233 if (out_len == nullptr) {
235 }
236 *out_len = 0;
237 if (!st.open) {
238 return Error::NotSupported;
239 }
240 if (rapdu == nullptr || out == nullptr) {
242 }
243 if (rapdu_len < (kMacFieldLen + 2U)) {
244 WipeChannel(st);
246 }
247
248 const std::uint8_t sw1 = rapdu[rapdu_len - 2U];
249 const std::uint8_t sw2 = rapdu[rapdu_len - 1U];
250 const std::size_t enc_len = rapdu_len - kMacFieldLen - 2U;
251
252 crypto::Cmac cm{};
253 crypto::CmacStart(cm, st.srmac);
254 crypto::CmacUpdate(cm, st.mcv, 16U);
255 crypto::CmacUpdate(cm, rapdu, enc_len);
256 crypto::CmacUpdate(cm, rapdu + rapdu_len - 2U, 2U);
257 std::uint8_t mac_full[16]{};
258 crypto::ScopedWipe wipe_mac(mac_full, sizeof(mac_full));
259 crypto::CmacFinish(cm, mac_full);
260 if (!crypto::ConstantTimeEqual(mac_full, rapdu + enc_len, kMacFieldLen)) {
261 WipeChannel(st);
263 }
264
265 if (enc_len == 0U) {
266 crypto::IncCommandCounter(st.counter);
267 if (out_cap < 2U) {
269 }
270 out[0] = sw1;
271 out[1] = sw2;
272 *out_len = 2U;
273 return Error::Ok;
274 }
275 if ((enc_len % 16U) != 0U) {
276 WipeChannel(st);
278 }
279 if (enc_len > out_cap) {
280 crypto::IncCommandCounter(st.counter); /* answer consumed: stay in step with the card */
282 }
283
284 std::uint8_t icv[16]{};
285 crypto::ScopedWipe wipe_icv(icv, sizeof(icv));
286 crypto::ResponseIcv(st.senc, st.counter, icv);
287 std::size_t plain_len = 0;
288 if (!crypto::Aes128CbcDecrypt(st.senc, icv, rapdu, out, enc_len) ||
289 !crypto::IsoUnpad80(out, enc_len, &plain_len)) {
290 crypto::SecureZero(out, enc_len);
291 WipeChannel(st);
293 }
294 crypto::IncCommandCounter(st.counter);
295 if ((plain_len + 2U) > out_cap) {
296 crypto::SecureZero(out, enc_len);
298 }
299 out[plain_len] = sw1;
300 out[plain_len + 1U] = sw2;
301 *out_len = plain_len + 2U;
302 return Error::Ok;
303}
304
305inline constexpr std::uint8_t kInsPutKey = 0xD8U;
306inline constexpr std::uint8_t kPutKeyTypeAes = 0x88U;
307inline constexpr std::uint8_t kPutKeyP2Multiple = 0x81U;
308inline constexpr std::uint8_t kPutKeyKcvLen = 0x03U;
309inline constexpr std::size_t kPutKeyBlockBytes = 23U;
310inline constexpr std::size_t kPutKeyDataBytes = 1U + 3U * kPutKeyBlockBytes;
311inline constexpr std::size_t kPutKeyExpectRspBytes = 1U + 3U * kPutKeyKcvLen;
312
321inline bool FillPutKeyBlock(std::uint8_t* block, const std::uint8_t current_dek[crypto::kAes128KeyLen],
322 const std::uint8_t plain[crypto::kAes128KeyLen],
323 std::uint8_t kcv3[kPutKeyKcvLen]) noexcept {
324 if (block == nullptr || current_dek == nullptr || plain == nullptr || kcv3 == nullptr) {
325 return false;
326 }
327 block[0] = kPutKeyTypeAes;
328 block[1] = static_cast<std::uint8_t>(crypto::kAes128KeyLen + 1U);
329 block[2] = static_cast<std::uint8_t>(crypto::kAes128KeyLen);
330 crypto::Aes128EncryptBlock(current_dek, plain, block + 3U);
331 std::uint8_t ones[crypto::kAes128KeyLen];
332 std::memset(ones, 0x01, sizeof(ones));
333 std::uint8_t kcv_full[crypto::kAes128KeyLen]{};
334 crypto::Aes128EncryptBlock(plain, ones, kcv_full);
336 std::memcpy(block + 3U + crypto::kAes128KeyLen + 1U, kcv_full, kPutKeyKcvLen);
337 std::memcpy(kcv3, kcv_full, kPutKeyKcvLen);
338 return true;
339}
340
349[[nodiscard]] inline Error BuildPutKey(std::uint8_t key_version,
350 const std::uint8_t current_dek[crypto::kAes128KeyLen],
351 const std::uint8_t new_enc[crypto::kAes128KeyLen],
352 const std::uint8_t new_mac[crypto::kAes128KeyLen],
353 const std::uint8_t new_dek[crypto::kAes128KeyLen],
354 std::uint8_t* capdu, std::size_t capdu_cap, std::size_t* capdu_len,
355 std::uint8_t* expect_rsp, std::size_t expect_cap,
356 std::size_t* expect_len) noexcept {
357 if (capdu_len == nullptr || expect_len == nullptr) {
359 }
360 *capdu_len = 0;
361 *expect_len = 0;
362 if (current_dek == nullptr || new_enc == nullptr || new_mac == nullptr || new_dek == nullptr ||
363 capdu == nullptr || expect_rsp == nullptr) {
365 }
366 if (expect_cap < kPutKeyExpectRspBytes) {
368 }
369 std::uint8_t data[kPutKeyDataBytes]{};
370 std::size_t off = 0;
371 data[off++] = key_version;
372 expect_rsp[0] = key_version;
373 std::size_t kcv_off = 1;
374 if (!FillPutKeyBlock(data + off, current_dek, new_enc, expect_rsp + kcv_off)) {
376 }
377 off += kPutKeyBlockBytes;
378 kcv_off += kPutKeyKcvLen;
379 if (!FillPutKeyBlock(data + off, current_dek, new_mac, expect_rsp + kcv_off)) {
381 }
382 off += kPutKeyBlockBytes;
383 kcv_off += kPutKeyKcvLen;
384 if (!FillPutKeyBlock(data + off, current_dek, new_dek, expect_rsp + kcv_off)) {
386 }
387 *expect_len = kPutKeyExpectRspBytes;
388 return apdu::BuildCaseShort(kClaGp, kInsPutKey, key_version, kPutKeyP2Multiple, data,
389 static_cast<std::uint8_t>(kPutKeyDataBytes), false, 0x00U, capdu, capdu_cap,
390 capdu_len);
391}
392
397template <typename TransportT>
398class Session {
399public:
400 Session() noexcept = default;
402 ~Session() { WipeChannel(st_); }
403 Session(const Session&) = delete;
404 Session& operator=(const Session&) = delete;
405
413 std::uint32_t timeout_ms) noexcept {
417 WipeChannel(st_);
418 failed_ = false;
419 if (!crypto::StaticKeysUsable(keys.host_static_key_enc, keys.host_static_key_enc_len,
420 keys.host_static_key_cmac, keys.host_static_key_cmac_len) ||
421 keys.host_challenge == nullptr || keys.host_challenge_len != kChallengeLen) {
422 return Error::NotSupported;
423 }
424
425 std::uint8_t capdu[16]{};
426 std::size_t capdu_len = 0;
427 Error e = apdu::BuildCaseShort(kClaGp, kInsInitializeUpdate, keys.key_version, 0x00U,
428 keys.host_challenge, static_cast<std::uint8_t>(kChallengeLen),
429 false, 0x00U, capdu, sizeof(capdu), &capdu_len);
430 if (e != Error::Ok) {
431 return e;
432 }
433
434 std::uint8_t rapdu[48]{};
435 std::size_t rapdu_len = 0;
436 e = t1.ExchangeInformation(capdu, capdu_len, rapdu, sizeof(rapdu), &rapdu_len, timeout_ms);
437 if (e != Error::Ok) {
438 return e;
439 }
440 if (rapdu_len < kInitUpdateRspMin) {
441 return Error::Protocol;
442 }
443 const std::uint8_t sw1 = rapdu[rapdu_len - 2U];
444 const std::uint8_t sw2 = rapdu[rapdu_len - 1U];
445 if (sw1 != 0x90U || sw2 != 0x00U) {
446 return Error::Protocol;
447 }
448
449 const std::uint8_t* card_challenge = rapdu + 13U;
450 const std::uint8_t* card_cryptogram = rapdu + 21U;
451
452 std::uint8_t senc[kAes128KeyLen]{};
453 std::uint8_t smac[kAes128KeyLen]{};
454 std::uint8_t srmac[kAes128KeyLen]{};
455 crypto::ScopedWipe wipe_senc(senc, sizeof(senc));
456 crypto::ScopedWipe wipe_smac(smac, sizeof(smac));
457 crypto::ScopedWipe wipe_srmac(srmac, sizeof(srmac));
458 if (!crypto::DeriveSessionKey(keys.host_static_key_enc, crypto::kDeriveSenc,
459 keys.host_challenge, card_challenge, senc) ||
460 !crypto::DeriveSessionKey(keys.host_static_key_cmac, crypto::kDeriveSmac,
461 keys.host_challenge, card_challenge, smac) ||
462 !crypto::DeriveSessionKey(keys.host_static_key_cmac, crypto::kDeriveSrmac,
463 keys.host_challenge, card_challenge, srmac)) {
465 }
466
467 std::uint8_t expect_card[kCryptogramLen]{};
468 if (!crypto::ComputeCryptogram(smac, crypto::kDeriveCardCryptogram, keys.host_challenge,
469 card_challenge, expect_card) ||
470 !crypto::ConstantTimeEqual(expect_card, card_cryptogram, kCryptogramLen)) {
471 return Error::Protocol;
472 }
473
474 std::uint8_t host_cryptogram[kCryptogramLen]{};
475 if (!crypto::ComputeCryptogram(smac, crypto::kDeriveHostCryptogram, keys.host_challenge,
476 card_challenge, host_cryptogram)) {
478 }
479
480 std::uint8_t ext[32]{};
481 ext[0] = kClaGpSecure;
484 ext[3] = 0x00U;
485 ext[4] = 0x10U;
486 std::memcpy(ext + 5, host_cryptogram, kCryptogramLen);
487
488 std::uint8_t mcv[16]{};
489 std::uint8_t mac_in[16 + 13]{};
490 std::uint8_t mac_full[16]{};
491 crypto::ScopedWipe wipe_mac(mac_full, sizeof(mac_full));
492 std::memcpy(mac_in, mcv, 16);
493 std::memcpy(mac_in + 16, ext, 13);
494 crypto::Aes128Cmac(smac, mac_in, 16U + 13U, mac_full);
495 std::memcpy(ext + 13, mac_full, kCryptogramLen);
496 const std::size_t ext_len = 5U + 2U * kCryptogramLen;
497
498 std::uint8_t ext_rapdu[16]{};
499 std::size_t ext_rapdu_len = 0;
500 e = t1.ExchangeInformation(ext, ext_len, ext_rapdu, sizeof(ext_rapdu), &ext_rapdu_len,
501 timeout_ms);
502 if (e != Error::Ok) {
503 return e;
504 }
505 if (ext_rapdu_len < 2U || ext_rapdu[ext_rapdu_len - 2U] != 0x90U ||
506 ext_rapdu[ext_rapdu_len - 1U] != 0x00U) {
507 return Error::Protocol;
508 }
509
510 std::memcpy(st_.senc, senc, kAes128KeyLen);
511 std::memcpy(st_.smac, smac, kAes128KeyLen);
512 std::memcpy(st_.srmac, srmac, kAes128KeyLen);
513 std::memcpy(st_.mcv, mac_full, 16);
514 std::memset(st_.counter, 0, sizeof(st_.counter));
515 st_.counter[15] = 0x01U;
516 st_.open = true;
517 return Error::Ok;
518 }
519
525 [[nodiscard]] Error Close() noexcept {
526 WipeChannel(st_);
527 failed_ = false;
528 return Error::Ok;
529 }
530
531 [[nodiscard]] bool IsOpen() const noexcept { return st_.open; }
532
538 [[nodiscard]] bool Failed() const noexcept { return failed_; }
539
541 void Abort() noexcept {
542 WipeChannel(st_);
543 failed_ = true;
544 }
545
546 [[nodiscard]] Error WrapCommand(const std::uint8_t* capdu, std::size_t capdu_len,
547 std::uint8_t* out, std::size_t out_cap,
548 std::size_t* out_len) noexcept {
549 return se050::scp03::WrapCommand(st_, capdu, capdu_len, out, out_cap, out_len);
550 }
551
552 [[nodiscard]] Error UnwrapResponse(const std::uint8_t* rapdu, std::size_t rapdu_len,
553 std::uint8_t* out, std::size_t out_cap,
554 std::size_t* out_len) noexcept {
555 const Error e = se050::scp03::UnwrapResponse(st_, rapdu, rapdu_len, out, out_cap, out_len);
556 if (e == Error::Authentication) {
557 failed_ = true;
558 }
559 return e;
560 }
561
562 [[nodiscard]] ChannelState& State() noexcept { return st_; }
563 [[nodiscard]] const ChannelState& State() const noexcept { return st_; }
564
565private:
566 ChannelState st_{};
567 bool failed_{false};
568};
569
570} // namespace se050::scp03
Stateful T=1 session bound to a concrete I²C transport.
Definition se050_t1_session.hpp:101
SCP03 session bound to a T=1 transport.
Definition se050_scp03.hpp:398
Session & operator=(const Session &)=delete
session keys are never copied
Error WrapCommand(const std::uint8_t *capdu, std::size_t capdu_len, std::uint8_t *out, std::size_t out_cap, std::size_t *out_len) noexcept
Definition se050_scp03.hpp:546
void Abort() noexcept
Mark the channel lost and wipe its keys (used by the device on a broken exchange).
Definition se050_scp03.hpp:541
Session() noexcept=default
Error Close() noexcept
Close the channel: wipe session keys, MAC chaining value and counter.
Definition se050_scp03.hpp:525
bool Failed() const noexcept
true after the channel was lost (a response failed its R-MAC, or the exchange broke inside the channe...
Definition se050_scp03.hpp:538
ChannelState & State() noexcept
Definition se050_scp03.hpp:562
const ChannelState & State() const noexcept
Definition se050_scp03.hpp:563
Error OpenSecureChannel(T1Session< TransportT > &t1, const StaticKeys &keys, std::uint32_t timeout_ms) noexcept
INITIALIZE UPDATE / EXTERNAL AUTHENTICATE.
Definition se050_scp03.hpp:412
Session(const Session &)=delete
session keys are never copied
Error UnwrapResponse(const std::uint8_t *rapdu, std::size_t rapdu_len, std::uint8_t *out, std::size_t out_cap, std::size_t *out_len) noexcept
Definition se050_scp03.hpp:552
bool IsOpen() const noexcept
Definition se050_scp03.hpp:531
Wipes a buffer with SecureZero when the scope ends, on every return path.
Definition se050_scp03_crypto.hpp:43
Error BuildCaseShort(std::uint8_t cla, std::uint8_t ins, std::uint8_t p1, std::uint8_t p2, const std::uint8_t *data, std::uint8_t data_len, bool le_present, std::uint8_t le, std::uint8_t *out, std::size_t out_cap, std::size_t *out_len) noexcept
Build a CASE 3/4 short command when data_len ≤ 255 and a 1-byte Le is enough.
Definition se050_apdu.hpp:106
constexpr std::size_t kChallengeLen
Definition se050_scp03_crypto.hpp:74
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
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
bool IsoPad80(std::uint8_t *buf, std::size_t *len, std::size_t cap)
Definition se050_scp03_crypto.hpp:488
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
constexpr std::size_t kCryptogramLen
Definition se050_scp03_crypto.hpp:75
constexpr std::size_t kAes128KeyLen
Definition se050_scp03_crypto.hpp:72
constexpr std::uint8_t kDeriveSrmac
Definition se050_scp03_crypto.hpp:82
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 kDeriveSmac
Definition se050_scp03_crypto.hpp:81
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
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
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
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
Definition se050_scp03.hpp:28
constexpr std::uint8_t kClaGpSecure
Definition se050_scp03.hpp:31
Error ParseCommand(const std::uint8_t *capdu, std::size_t len, CommandFields *f) noexcept
Split a serialized C-APDU into data and Le (ISO/IEC 7816-4 section 5.1, cases 1, 2S/E,...
Definition se050_scp03.hpp:78
constexpr std::uint8_t kDefaultKeyVersion
Definition se050_scp03.hpp:34
void WipeChannel(ChannelState &st) noexcept
End a session: wipe the session keys, MAC chaining value and counter, mark it closed.
Definition se050_scp03.hpp:219
constexpr std::size_t kMacFieldLen
Definition se050_scp03.hpp:38
constexpr std::size_t kPutKeyBlockBytes
type + lens + enc key + KCV
Definition se050_scp03.hpp:309
constexpr std::uint8_t kClaSmBit
Definition se050_scp03.hpp:37
constexpr std::uint8_t kInsExternalAuthenticate
Definition se050_scp03.hpp:33
bool FillPutKeyBlock(std::uint8_t *block, const std::uint8_t current_dek[crypto::kAes128KeyLen], const std::uint8_t plain[crypto::kAes128KeyLen], std::uint8_t kcv3[kPutKeyKcvLen]) noexcept
One AES key field inside GP PUT KEY (encrypted with the current DEK).
Definition se050_scp03.hpp:321
constexpr std::uint8_t kPutKeyKcvLen
Definition se050_scp03.hpp:308
constexpr std::uint8_t kInsInitializeUpdate
Definition se050_scp03.hpp:32
constexpr std::uint8_t kPutKeyTypeAes
Definition se050_scp03.hpp:306
Error WrapCommand(ChannelState &st, const std::uint8_t *capdu, std::size_t capdu_len, std::uint8_t *out, std::size_t out_cap, std::size_t *out_len) noexcept
C-MAC + C-ENC wrap of a C-APDU in any ISO/IEC 7816-4 case, short or extended.
Definition se050_scp03.hpp:132
constexpr std::size_t kPutKeyExpectRspBytes
version + 3 KCVs
Definition se050_scp03.hpp:311
constexpr std::size_t kPutKeyDataBytes
version + ENC/MAC/DEK
Definition se050_scp03.hpp:310
constexpr std::uint8_t kPutKeyP2Multiple
Definition se050_scp03.hpp:307
constexpr std::uint8_t kClaGp
Definition se050_scp03.hpp:30
Error UnwrapResponse(ChannelState &st, const std::uint8_t *rapdu, std::size_t rapdu_len, std::uint8_t *out, std::size_t out_cap, std::size_t *out_len) noexcept
R-MAC check and R-ENC decryption of one response. Increments the counter on success.
Definition se050_scp03.hpp:230
constexpr std::uint8_t kInsPutKey
Definition se050_scp03.hpp:305
Error BuildPutKey(std::uint8_t key_version, const std::uint8_t current_dek[crypto::kAes128KeyLen], const std::uint8_t new_enc[crypto::kAes128KeyLen], const std::uint8_t new_mac[crypto::kAes128KeyLen], const std::uint8_t new_dek[crypto::kAes128KeyLen], std::uint8_t *capdu, std::size_t capdu_cap, std::size_t *capdu_len, std::uint8_t *expect_rsp, std::size_t expect_cap, std::size_t *expect_len) noexcept
Build GP PUT KEY (CLA 0x80, INS 0xD8, P1 = key version, P2 0x81).
Definition se050_scp03.hpp:349
constexpr std::size_t kWrapScratchBytes
Minimum wrap workspace, enough for short APDUs (CM4: D2 .se_apdu_scratch, not task stack).
Definition se050_scp03.hpp:40
constexpr std::size_t kInitUpdateRspMin
Definition se050_scp03.hpp:36
constexpr std::uint8_t kSecLevelCmacCencRmacRenc
Definition se050_scp03.hpp:35
constexpr std::size_t kWrapScratchBytesExtended
Wrap workspace for any command up to kMaxApduCommandBytes (extended header, padding,...
Definition se050_scp03.hpp:64
Error
Definition se050_types.hpp:14
constexpr std::size_t kMaxApduCommandBytes
Definition se050_types.hpp:40
ISO 7816-4 C-APDU packing and R-APDU parsing helpers (short + extended length).
Host AES-128 / RFC 4493 CMAC / GlobalPlatform SCP03 KDF (header-only).
ISO/IEC 7816-3 T=1 block protocol for NXP SE050 T=1 over I²C (EDC = CRC-16).
Shared types, limits, and error codes for the HF-SE050 driver.
Live SCP03 session state after a successful handshake.
Definition se050_scp03.hpp:54
std::uint8_t srmac[crypto::kAes128KeyLen]
Definition se050_scp03.hpp:58
std::uint8_t smac[crypto::kAes128KeyLen]
Definition se050_scp03.hpp:57
bool open
Definition se050_scp03.hpp:55
std::uint8_t mcv[16]
Definition se050_scp03.hpp:59
std::uint8_t senc[crypto::kAes128KeyLen]
Definition se050_scp03.hpp:56
std::uint8_t counter[16]
Definition se050_scp03.hpp:60
A C-APDU split into its ISO/IEC 7816-4 section 5.1 fields.
Definition se050_scp03.hpp:67
bool has_le
an Le field was present (cases 2 and 4)
Definition se050_scp03.hpp:71
const std::uint8_t * data
Definition se050_scp03.hpp:68
std::size_t data_len
Definition se050_scp03.hpp:69
bool extended
Lc / Le were in extended form (cases 2E, 3E, 4E)
Definition se050_scp03.hpp:70
Runtime static keys (never compiled-in NXP defaults).
Definition se050_scp03.hpp:43
std::size_t host_static_key_cmac_len
Definition se050_scp03.hpp:47
const std::uint8_t * host_challenge
Definition se050_scp03.hpp:48
std::uint8_t key_version
Definition se050_scp03.hpp:50
const std::uint8_t * host_static_key_cmac
Definition se050_scp03.hpp:46
std::size_t host_challenge_len
Definition se050_scp03.hpp:49
std::size_t host_static_key_enc_len
Definition se050_scp03.hpp:45
const std::uint8_t * host_static_key_enc
Definition se050_scp03.hpp:44
Streaming AES-128-CMAC (RFC 4493) — no concatenated MAC buffer.
Definition se050_scp03_crypto.hpp:512