30inline constexpr std::uint8_t
kClaGp = 0x80U;
59 std::uint8_t
mcv[16]{};
68 const std::uint8_t*
data{
nullptr};
79 if (capdu ==
nullptr || f ==
nullptr || len < 4U) {
90 if (capdu[4] != 0x00U) {
91 const std::size_t lc = capdu[4];
92 if (len == 5U + lc || len == 6U + lc) {
95 f->has_le = (len == 6U + lc);
105 const std::size_t lc = (
static_cast<std::size_t
>(capdu[5]) << 8) | capdu[6];
109 if (len == 7U + lc || len == 9U + lc) {
110 f->data = capdu + 7U;
112 f->has_le = (len == 9U + lc);
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) {
142 if (capdu ==
nullptr || out ==
nullptr || capdu_len < 4U) {
156 const std::size_t enc_len = ((f.data_len / 16U) + 1U) * 16U;
158 const bool extended = f.extended || lc > 255U;
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) {
168 out[0] =
static_cast<std::uint8_t
>(capdu[0] |
kClaSmBit);
174 out[5] =
static_cast<std::uint8_t
>((lc >> 8) & 0xFFU);
175 out[6] =
static_cast<std::uint8_t
>(lc & 0xFFU);
177 out[4] =
static_cast<std::uint8_t
>(lc);
181 if (f.data_len > 0U) {
182 std::memmove(out + hdr, f.data, f.data_len);
184 std::size_t padded = f.data_len;
189 std::uint8_t icv[16]{};
202 std::uint8_t mac_full[16]{};
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) {
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) {
240 if (rapdu ==
nullptr || out ==
nullptr) {
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;
257 std::uint8_t mac_full[16]{};
275 if ((enc_len % 16U) != 0U) {
279 if (enc_len > out_cap) {
284 std::uint8_t icv[16]{};
287 std::size_t plain_len = 0;
295 if ((plain_len + 2U) > out_cap) {
299 out[plain_len] = sw1;
300 out[plain_len + 1U] = sw2;
301 *out_len = plain_len + 2U;
324 if (block ==
nullptr || current_dek ==
nullptr || plain ==
nullptr || kcv3 ==
nullptr) {
332 std::memset(ones, 0x01,
sizeof(ones));
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) {
362 if (current_dek ==
nullptr || new_enc ==
nullptr || new_mac ==
nullptr || new_dek ==
nullptr ||
363 capdu ==
nullptr || expect_rsp ==
nullptr) {
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)) {
379 if (!
FillPutKeyBlock(data + off, current_dek, new_mac, expect_rsp + kcv_off)) {
384 if (!
FillPutKeyBlock(data + off, current_dek, new_dek, expect_rsp + kcv_off)) {
389 static_cast<std::uint8_t
>(
kPutKeyDataBytes),
false, 0x00U, capdu, capdu_cap,
397template <
typename TransportT>
413 std::uint32_t timeout_ms)
noexcept {
420 keys.host_static_key_cmac, keys.host_static_key_cmac_len) ||
421 keys.host_challenge ==
nullptr || keys.host_challenge_len != kChallengeLen) {
425 std::uint8_t capdu[16]{};
426 std::size_t capdu_len = 0;
428 keys.host_challenge,
static_cast<std::uint8_t
>(kChallengeLen),
429 false, 0x00U, capdu,
sizeof(capdu), &capdu_len);
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);
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) {
449 const std::uint8_t* card_challenge = rapdu + 13U;
450 const std::uint8_t* card_cryptogram = rapdu + 21U;
452 std::uint8_t senc[kAes128KeyLen]{};
453 std::uint8_t smac[kAes128KeyLen]{};
454 std::uint8_t srmac[kAes128KeyLen]{};
459 keys.host_challenge, card_challenge, senc) ||
461 keys.host_challenge, card_challenge, smac) ||
463 keys.host_challenge, card_challenge, srmac)) {
467 std::uint8_t expect_card[kCryptogramLen]{};
469 card_challenge, expect_card) ||
474 std::uint8_t host_cryptogram[kCryptogramLen]{};
476 card_challenge, host_cryptogram)) {
480 std::uint8_t ext[32]{};
486 std::memcpy(ext + 5, host_cryptogram, kCryptogramLen);
488 std::uint8_t mcv[16]{};
489 std::uint8_t mac_in[16 + 13]{};
490 std::uint8_t mac_full[16]{};
492 std::memcpy(mac_in, mcv, 16);
493 std::memcpy(mac_in + 16, ext, 13);
495 std::memcpy(ext + 13, mac_full, kCryptogramLen);
496 const std::size_t ext_len = 5U + 2U * kCryptogramLen;
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,
505 if (ext_rapdu_len < 2U || ext_rapdu[ext_rapdu_len - 2U] != 0x90U ||
506 ext_rapdu[ext_rapdu_len - 1U] != 0x00U) {
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);
531 [[nodiscard]]
bool IsOpen() const noexcept {
return st_.
open; }
538 [[nodiscard]]
bool Failed() const noexcept {
return failed_; }
547 std::uint8_t* out, std::size_t out_cap,
548 std::size_t* out_len)
noexcept {
553 std::uint8_t* out, std::size_t out_cap,
554 std::size_t* out_len)
noexcept {
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