Add SL2 for MIFARE Plus (WIP)

This commit is contained in:
GOB
2026-01-21 16:12:05 +09:00
parent a2e79eea99
commit ed8935ce02
6 changed files with 315 additions and 38 deletions
+4 -2
View File
@@ -192,8 +192,10 @@ namespace plus {
*/
using AESKey = std::array<uint8_t, 16>;
//! @brief Default key for MIFARE Plus
constexpr AESKey DEFAULT_KEY{}; // All 0x00
constexpr AESKey DEFAULT_KEY{}; // All 0x00
//! @brief Default AES sector key (as classic DEFAULT_KEY)
constexpr AESKey DEFAULT_FF_KEY = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
/*!
@struct Keys
+22 -2
View File
@@ -121,6 +121,12 @@ inline bool is_mifare_plus(const Type t)
return t >= Type::MIFARE_Plus_2K && t <= Type::MIFARE_Plus_SE;
}
//! @brief Is type MIFARE classic compatible? (Plus SL1)
inline bool is_mifare_classic_compatible(const Type t, const uint8_t sl)
{
return is_mifare_plus(t) && (sl == 1);
}
//! @brief Is type MIFARE DESFire?
inline bool is_mifare_desfire(const Type t)
{
@@ -349,10 +355,10 @@ struct PICC {
{
return is_mifare(type);
}
//! @brief Is MIFARE classic? (include Plus SL1)
//! @brief Is MIFARE classic? (include Plus compatible SL)
inline bool isMifareClassic() const
{
return is_mifare_classic(type) || (is_mifare_plus(type) && security_level == 1);
return is_mifare_classic(type) || isMifareClassicCompatible();
}
//! @brief Is MIFARE Ultralight series?
inline bool isMifareUltralight() const
@@ -364,6 +370,20 @@ struct PICC {
{
return is_mifare_plus(type);
}
//! @brief Is MIFARE Plus X?
inline bool isMifarePlusX() const
{
return is_mifare_plus(type) && sub_type_plus == SubTypePlus::X;
}
//! @brief Is MIFARE Plus S?
inline bool isMifarePlusS() const
{
return is_mifare_plus(type) && sub_type_plus == SubTypePlus::S;
}
inline bool isMifareClassicCompatible() const
{
return is_mifare_classic_compatible(type, security_level);
}
//! @brief Is MIFARE DESFire?
inline bool isMifareDESFire() const
{
+20 -20
View File
@@ -246,7 +246,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
// Send I-Block and receive first frame
if (!_layer.transceive(rx_buf, rlen, tx_buf, tpos, timeout_ms)) {
if (retries++ < _cfg.max_retries) continue;
M5_LIB_LOGE(">>>>ERROR 1");
// M5_LIB_LOGE(">>>>ERROR 1");
return false;
}
@@ -257,7 +257,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
// resend I-Block
break;
}
M5_LIB_LOGE(">>>>ERROR 2");
// M5_LIB_LOGE(">>>>ERROR 2");
return false;
}
if (_cfg.rx_crc && rlen >= 3) {
@@ -270,7 +270,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
// resend I-Block
break;
}
M5_LIB_LOGE(">>>>ERROR 3");
// M5_LIB_LOGE(">>>>ERROR 3");
return false;
}
@@ -284,13 +284,13 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
if (retries++ < _cfg.max_retries) {
break; // resend I-Block
}
M5_LIB_LOGE(">>>>ERROR 4");
// M5_LIB_LOGE(">>>>ERROR 4");
return false;
}
const uint8_t wtxm = get_wtxm(rx_buf[rx_overhead_min]);
if (!is_valid_wtxm(wtxm)) {
M5_LIB_LOGE(">>>>ERROR 5");
// M5_LIB_LOGE(">>>>ERROR 5");
return false;
}
@@ -308,7 +308,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
if (retries++ < _cfg.max_retries) {
break; // resend I-Block
}
M5_LIB_LOGE(">>>>ERROR 6");
// M5_LIB_LOGE(">>>>ERROR 6");
return false;
}
// Parse the newly received frame in the same loop
@@ -320,7 +320,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
if (retries++ < _cfg.max_retries) {
break; // resend I-Block
}
M5_LIB_LOGE(">>>>ERROR 7");
// M5_LIB_LOGE(">>>>ERROR 7");
return false;
}
@@ -330,7 +330,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
if (retries++ < _cfg.max_retries) {
break; // resend I-Block
}
M5_LIB_LOGE(">>>>ERROR 8");
// M5_LIB_LOGE(">>>>ERROR 8");
return false;
}
@@ -338,7 +338,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
if (retries++ < _cfg.max_retries) {
break; // resend I-Block
}
M5_LIB_LOGE(">>>>ERROR 9");
// M5_LIB_LOGE(">>>>ERROR 9");
return false;
}
@@ -352,7 +352,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
if (retries++ < _cfg.max_retries) {
break;
}
M5_LIB_LOGE(">>>>ERROR 10");
// M5_LIB_LOGE(">>>>ERROR 10");
return false;
}
@@ -362,14 +362,14 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
if (_cfg.use_cid) idx++;
if (_cfg.use_nad) idx++;
if (rlen < idx) {
M5_LIB_LOGE(">>>>ERROR 11");
// M5_LIB_LOGE(">>>>ERROR 11");
return false;
}
const uint16_t inf_len = (uint16_t)(rlen - idx);
if (rx_written + inf_len > rx_inf_len) {
M5_LIB_LOGE("rx_written %u inf_len %u rx_inf_len %u", rx_written, inf_len, rx_inf_len);
m5::utility::log::dump(rx_inf, rx_written, false);
// M5_LIB_LOGE("rx_written %u inf_len %u rx_inf_len %u", rx_written, inf_len, rx_inf_len);
// m5::utility::log::dump(rx_inf, rx_written, false);
return false;
}
@@ -388,7 +388,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
uint16_t rlen2 = sizeof(rx_buf);
if (!_layer.transceive(rx_buf, rlen2, r_ack, rp, _cfg.fwt_ms)) {
M5_LIB_LOGE(">>>>ERROR 12");
// M5_LIB_LOGE(">>>>ERROR 12");
return false;
}
@@ -401,7 +401,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
// If any S-Block other than WTX exists within the chain,
// it shall be treated as unsupported and result in failure.
if (is_s_block(pcb2) && !is_s_wtx(pcb2)) {
M5_LIB_LOGE(">>>>ERROR 13");
// M5_LIB_LOGE(">>>>ERROR 13");
return false;
}
@@ -423,7 +423,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
rlen2 = sizeof(rx_buf);
if (!_layer.transceive(rx_buf, rlen2, s_ack, sp, wtx_timeout)) {
M5_LIB_LOGE(">>>>ERROR 14");
// M5_LIB_LOGE(">>>>ERROR 14");
return false;
}
continue;
@@ -436,7 +436,7 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
// ACK: receive again
rlen2 = sizeof(rx_buf);
if (!_layer.receive(rx_buf, rlen2, _cfg.fwt_ms)) {
M5_LIB_LOGE(">>>>ERROR 15");
// M5_LIB_LOGE(">>>>ERROR 15");
return false;
}
continue;
@@ -449,9 +449,9 @@ bool IsoDEP::transceiveINF(uint8_t* rx_inf, uint16_t& rx_inf_len, const uint8_t*
const uint16_t inf_len2 = (uint16_t)(rlen2 - idx2);
if (rx_written + inf_len2 > rx_inf_len) {
M5_LIB_LOGE("rx_written %u inf_len2 %u rx_inf_len %u", rx_written, inf_len2,
rx_inf_len);
m5::utility::log::dump(rx_inf, rx_written, false);
// M5_LIB_LOGE("rx_written %u inf_len2 %u rx_inf_len %u", rx_written, inf_len2,
// rx_inf_len);
// m5::utility::log::dump(rx_inf, rx_written, false);
return false;
}
+1 -5
View File
@@ -33,10 +33,7 @@ inline uint16_t fsci_to_fsc(const uint8_t fsci)
struct config_t {
#if 0
// PICCが受けられる最大INFFSC
uint16_t fsc{256};
// PCD側の送受信制約(Unit依存: FIFO/内部バッファ)
// ISO-DEP フレーム全体(PCB+CID/NAD+INF)をこのサイズ以内に収める
uint16_t pcd_max_frame_tx{256};
uint16_t pcd_max_frame_rx{256};
#else
@@ -44,11 +41,10 @@ struct config_t {
uint16_t pcd_max_frame_tx{64};
uint16_t pcd_max_frame_rx{64};
#endif
// 待ち(FWT/WTX)は後で詰められるように一旦msで持つ
uint32_t fwt_ms{100};
uint32_t wtx_max_ms{5000};
// ISO-DEPオプション
// optios
bool use_cid{};
uint8_t cid{};
bool use_nad{};
+229 -6
View File
@@ -15,6 +15,7 @@
#include <inttypes.h>
#include <M5Utility.hpp>
#include <algorithm>
#include <mbedtls/aes.h>
#include <esp_random.h>
using namespace m5::nfc;
@@ -75,6 +76,11 @@ void rotate_byte_left(uint8_t out[8], const uint8_t in[8])
out[7] = in[0];
}
uint16_t mifare_plus_key_no(const uint8_t sector, const bool key_b)
{
return static_cast<uint16_t>(0x4000 + sector * 2 + (key_b ? 1 : 0));
}
constexpr int8_t kAccessDenied{-1};
constexpr int8_t kAccessFree{-2};
int8_t required_read_key_no_from_access_rights(const uint16_t access_rights)
@@ -195,7 +201,7 @@ bool NFCLayerA::select(m5::nfc::a::PICC& picc)
{
_activePICC = PICC{};
if (_impl->select(picc)) {
if (picc.isISO14443_4() && !(picc.isMifarePlus() && picc.security_level == 1)) {
if (picc.isISO14443_4() && !picc.isMifareClassicCompatible()) {
if (!nfca_request_ats(picc.ats)) {
return false;
}
@@ -206,11 +212,13 @@ bool NFCLayerA::select(m5::nfc::a::PICC& picc)
return false;
}
bool NFCLayerA::activate(const PICC& picc)
bool NFCLayerA::activate(const PICC& picc, const bool force_rats)
{
_activePICC = PICC{};
if (_impl->activate(picc)) {
if (picc.isISO14443_4() && !(picc.isMifarePlus() && picc.security_level == 1)) {
M5_LIB_LOGE(" >>>> SEL");
if (force_rats || (picc.isISO14443_4() && !picc.isMifareClassicCompatible())) {
M5_LIB_LOGE(" >>>> RATS");
ATS discard{};
if (!nfca_request_ats(discard)) {
M5_LIB_LOGE("Failed to RATS");
@@ -225,7 +233,7 @@ bool NFCLayerA::activate(const PICC& picc)
return false;
}
bool NFCLayerA::reactivate(const PICC& picc)
bool NFCLayerA::reactivate(const PICC& picc, const bool force_rats)
{
PICC tmp = picc;
if (_activePICC.valid()) {
@@ -239,7 +247,7 @@ bool NFCLayerA::reactivate(const PICC& picc)
M5_LIB_LOGE("Failed to wakeup");
return false;
}
if (!activate(tmp)) {
if (!activate(tmp, force_rats)) {
M5_LIB_LOGE("Failed to activate");
return false;
}
@@ -252,7 +260,7 @@ bool NFCLayerA::deactivate()
_activePICC = PICC{};
auto ret = false;
if (tmp.isMifarePlus() && tmp.security_level == 1) {
if (tmp.isMifareClassicCompatible()) {
ret = _impl->hlt();
} else {
ret = tmp.isISO14443_4() ? nfca_deselect() : _impl->hlt();
@@ -703,6 +711,8 @@ bool NFCLayerA::dump(const Key& mkey)
if (_activePICC.valid()) {
if (_activePICC.isMifareClassic()) {
return dump_sector_structure(_activePICC, mkey);
} else if (_activePICC.isMifarePlus() && _activePICC.security_level == 2) {
return dump_mifare_plus_sl2(m5::nfc::a::mifare::plus::DEFAULT_KEY);
} else if (_activePICC.supportsNFC()) {
return dump_page_structure(_activePICC.blocks);
} else if (_activePICC.isMifareDESFire()) {
@@ -713,6 +723,17 @@ bool NFCLayerA::dump(const Key& mkey)
return false;
}
bool NFCLayerA::dump(const m5::nfc::a::mifare::plus::AESKey& key)
{
if (_activePICC.valid()) {
if (_activePICC.isMifarePlus() && _activePICC.security_level == 2) {
return dump_mifare_plus_sl2(key);
}
M5_LIB_LOGW("Not supported %s", _activePICC.typeAsString().c_str());
}
return false;
}
bool NFCLayerA::dump(const uint8_t block)
{
if (_activePICC.valid()) {
@@ -1007,7 +1028,9 @@ bool NFCLayerA::mifareUltralightCAuthenticate(const uint8_t key[16])
bool NFCLayerA::mifarePlusUpgradeSecurityLevel1(const mifare::plus::AESKey& card_config_key,
const mifare::plus::AESKey& card_master_key,
const mifare::plus::AESKey& l2_switch_key,
const mifare::plus::AESKey& l3_switch_key,
const mifare::plus::AESKey& aes_sector_key,
const mifare::classic::Key& key_a, const mifare::classic::Key& key_b)
{
if (!_activePICC.valid() || !_activePICC.isMifarePlus() || _activePICC.security_level != 0) {
@@ -1053,10 +1076,26 @@ bool NFCLayerA::mifarePlusUpgradeSecurityLevel1(const mifare::plus::AESKey& card
if (!write_perso_block(0x9000, card_master_key.data())) {
return false;
}
if ((_activePICC.sub_type == m5::stl::to_underlying(SubTypePlus::EV2) ||
_activePICC.sub_type == m5::stl::to_underlying(SubTypePlus::X)) &&
!write_perso_block(0x9002, l2_switch_key.data())) {
return false;
}
if (!write_perso_block(0x9003, l3_switch_key.data())) {
return false;
}
for (uint16_t sector = 0; sector < sectors; ++sector) {
const uint16_t key_a_no = mifare_plus_key_no((uint8_t)sector, false);
const uint16_t key_b_no = mifare_plus_key_no((uint8_t)sector, true);
if (!write_perso_block(key_a_no, aes_sector_key.data())) {
return false;
}
if (!write_perso_block(key_b_no, aes_sector_key.data())) {
return false;
}
}
for (uint16_t sector = 0; sector < sectors; ++sector) {
const uint16_t st_block = get_sector_trailer_block_from_sector(sector);
if (!write_perso_block(st_block, block)) {
@@ -1076,6 +1115,24 @@ bool NFCLayerA::mifarePlusUpgradeSecurityLevel1(const mifare::plus::AESKey& card
return true;
}
bool NFCLayerA::mifarePlusUpgradeSecurityLevel2(const mifare::plus::AESKey& sl2_switch_key)
{
if (!_activePICC.valid() || !_activePICC.isMifarePlus() || _activePICC.security_level != 1) {
return false;
}
if (!(_activePICC.sub_type == m5::stl::to_underlying(SubTypePlus::EV2) ||
_activePICC.sub_type == m5::stl::to_underlying(SubTypePlus::X))) {
return false;
}
if (!mifare_plus_authenticateAES(0x9002, sl2_switch_key)) {
M5_LIB_LOGE("SL2 auth failed");
return false;
}
_activePICC.security_level = 2;
return true;
}
bool NFCLayerA::ndefIsValidFormat(bool& valid)
{
valid = false;
@@ -1237,6 +1294,67 @@ bool NFCLayerA::dump_page(const uint8_t page, uint16_t maxPage)
return false;
}
bool NFCLayerA::mifare_plus_read_plain_nomac(const uint16_t block, const uint8_t count, std::vector<uint8_t>& out)
{
out.clear();
if (count == 0 || count > 3) {
return false;
}
uint8_t tx[] = {0x36, (uint8_t)(block & 0xFF), (uint8_t)((block >> 8) & 0xFF), count};
uint8_t rx[64]{};
uint16_t rx_len = sizeof(rx);
if (!_isoDEP.transceiveINF(rx, rx_len, tx, sizeof(tx)) || rx_len < 1) {
return false;
}
const bool has_status = (rx[0] == 0x90 || rx[0] == 0xAF);
const uint8_t* payload = has_status ? rx + 1 : rx;
size_t pay_len = has_status ? (rx_len - 1) : rx_len;
if (pay_len >= 2 && payload[pay_len - 2] == 0x90 && payload[pay_len - 1] == 0x00) {
pay_len -= 2;
}
if (pay_len < static_cast<size_t>(count) * 16) {
return false;
}
out.insert(out.end(), payload, payload + (size_t)count * 16);
return true;
}
bool NFCLayerA::dump_mifare_plus_sl2(const m5::nfc::a::mifare::plus::AESKey& key)
{
uint8_t sectors = get_number_of_sectors(_activePICC.type);
if (!sectors) {
return false;
}
puts(
"Sec[Blk]:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F [Access]\n"
"-----------------------------------------------------------------");
for (uint8_t sector = 0; sector < sectors; ++sector) {
const uint8_t blocks = (sector < 32) ? 4U : 16U;
const uint16_t base = (sector < 32) ? sector * blocks : 128U + (sector - 32) * blocks;
const uint16_t key_no = mifare_plus_key_no(sector, false);
if (!mifare_plus_authenticateAES(key_no, key)) {
M5_LIB_LOGE("SL2 auth failed sector %u key_no %04X", sector, key_no);
return false;
}
for (uint8_t i = 0; i < blocks; i += 3) {
const uint16_t block = base + i;
const uint8_t cnt = std::min<uint8_t>(3U, blocks - i);
std::vector<uint8_t> data{};
if (!mifare_plus_read_plain_nomac(block, cnt, data)) {
M5_LIB_LOGE("SL2 read failed block %u", block);
return false;
}
for (uint8_t j = 0; j < cnt; ++j) {
print_block(data.data() + j * 16, block + j, (j == 0) ? sector : -1, 0x00, false, false);
}
}
}
return true;
}
bool NFCLayerA::dump_desfire()
{
const auto cfg = _isoDEP.config();
@@ -1770,6 +1888,111 @@ bool NFCLayerA::mifare_get_version_L4_raw(uint8_t* ver, uint16_t& ver_len)
return ver_len >= 6;
}
bool NFCLayerA::mifare_plus_authenticateAES(const uint16_t key_no, const mifare::plus::AESKey& key)
{
uint8_t rx[256]{};
uint16_t rx_len{};
// Step 1: 0x70 KeyNo LSB/MSB 0x00
uint8_t cmd1[] = {0x70, (uint8_t)(key_no & 0xFF), (uint8_t)(key_no >> 8), 0x00};
rx_len = sizeof(rx);
if (!_isoDEP.transceiveINF(rx, rx_len, cmd1, sizeof(cmd1)) || rx_len < 1) {
M5_LIB_LOGE("AuthAES step1 transceive failed");
return false;
}
if ((rx[0] != 0x90 && rx[0] != 0xAF) || rx_len < 17) {
M5_LIB_LOGE("AuthAES step1 invalid response len=%u st=%02X", rx_len, rx[0]);
m5::utility::log::dump(rx, rx_len, false);
return false;
}
const bool step1_has_status = (rx[0] == 0x90 || rx[0] == 0xAF);
const uint8_t* step1_payload = step1_has_status ? rx + 1 : rx;
size_t step1_len = step1_has_status ? (rx_len - 1) : rx_len;
if (step1_len >= 2 && step1_payload[step1_len - 2] == 0x90 && step1_payload[step1_len - 1] == 0x00) {
step1_len -= 2;
}
if (step1_len < 16) {
M5_LIB_LOGE("AuthAES step1 payload too short len=%u", (unsigned)step1_len);
return false;
}
uint8_t rndB[16]{};
{
uint8_t iv[16]{};
mbedtls_aes_context aes{};
mbedtls_aes_init(&aes);
mbedtls_aes_setkey_dec(&aes, key.data(), 128);
mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, sizeof(rndB), iv, step1_payload, rndB);
mbedtls_aes_free(&aes);
}
uint8_t rndA[16]{};
for (auto& b : rndA) {
b = (uint8_t)(esp_random() & 0xFF);
}
uint8_t rndB_rot[16]{};
memcpy(rndB_rot, rndB + 1, 15);
rndB_rot[15] = rndB[0];
uint8_t ab_plain[32]{};
memcpy(ab_plain, rndA, 16);
memcpy(ab_plain + 16, rndB_rot, 16);
uint8_t cmd2[33]{};
cmd2[0] = 0x72;
{
uint8_t iv[16]{};
mbedtls_aes_context aes{};
mbedtls_aes_init(&aes);
mbedtls_aes_setkey_enc(&aes, key.data(), 128);
mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, sizeof(ab_plain), iv, ab_plain, cmd2 + 1);
mbedtls_aes_free(&aes);
}
rx_len = sizeof(rx);
if (!_isoDEP.transceiveINF(rx, rx_len, cmd2, sizeof(cmd2)) || rx_len < 1) {
M5_LIB_LOGE("AuthAES step2 transceive failed");
return false;
}
if ((rx[0] != 0x90 && rx[0] != 0xAF) || rx_len < 33) {
M5_LIB_LOGE("AuthAES step2 invalid response len=%u st=%02X", rx_len, rx[0]);
m5::utility::log::dump(rx, rx_len, false);
return false;
}
const bool step2_has_status = (rx[0] == 0x90 || rx[0] == 0xAF);
const uint8_t* step2_payload = step2_has_status ? rx + 1 : rx;
size_t step2_len = step2_has_status ? (rx_len - 1) : rx_len;
if (step2_len >= 2 && step2_payload[step2_len - 2] == 0x90 && step2_payload[step2_len - 1] == 0x00) {
step2_len -= 2;
}
if (step2_len < 32) {
M5_LIB_LOGE("AuthAES step2 payload too short len=%u", (unsigned)step2_len);
return false;
}
uint8_t ab_resp[32]{};
{
uint8_t iv[16]{};
mbedtls_aes_context aes{};
mbedtls_aes_init(&aes);
mbedtls_aes_setkey_dec(&aes, key.data(), 128);
mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, sizeof(ab_resp), iv, step2_payload, ab_resp);
mbedtls_aes_free(&aes);
}
uint8_t rndA_rot[16]{};
memcpy(rndA_rot, rndA + 1, 15);
rndA_rot[15] = rndA[0];
if (memcmp(ab_resp + 4, rndA_rot, 16) != 0) {
M5_LIB_LOGE("AuthAES rndA mismatch");
return false;
}
return true;
}
bool NFCLayerA::ntag_read_page(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage)
{
if (!rx || !rx_len || spage > epage || !_activePICC.valid()) {
+39 -3
View File
@@ -131,18 +131,20 @@ public:
/*!
@brief Activate a specific PICC (anti-collision against the given PICC)
@param picc PICC
@param force_rats Force Request_rats (For Plus SL1/2)
@return True if successful
@pre PICC is READY state
@post PICC transitions: READY -> ACTIVE on successful response
*/
bool activate(const m5::nfc::a::PICC& picc);
bool activate(const m5::nfc::a::PICC& picc, const bool force_rats = false);
/*!
@brief Wake and activate a specific PICC by PICC
@param picc Target PICC
@param force_rats Force Request_rats (For Plus SL1/2)
@return True if successful
@post PICC transitions: IDLE/HALT -> READY -> ACTIVE on a successful sequence
*/
bool reactivate(const m5::nfc::a::PICC& picc);
bool reactivate(const m5::nfc::a::PICC& picc, const bool force_rats = false);
/*!
@brief Reactivate the previously selected PICC
@details This function attempts to recover communication with the currently stored
@@ -253,6 +255,7 @@ public:
@pre All blocks must be authenticatable using the specified key if MIFARE classic
*/
bool dump(const m5::nfc::a::mifare::classic::Key& mkey = m5::nfc::a::mifare::classic::DEFAULT_KEY);
bool dump(const m5::nfc::a::mifare::plus::AESKey& key);
/*!
@brief Dump 1 block
@param addr Block address
@@ -427,7 +430,9 @@ public:
@brief Upgrade security level to SL1 (Classic compatibility mode)
@param card_config_key Card Configuration Key (AES)
@param card_master_key Card Master Key (AES)
@param l3_switch_key Level 3 Switch Key (AES)
@param l2_switch_key Level 2 Switch Key (AES, for MIFARE Plus X/EV2 SL2 only)
@param l3_switch_key Level 3 Switch Key (AES, for SL3)
@param aes_sector_key AES sector key (for SL2/SL3, written to 0x4000+)
@param key_a Crypto1 Key A (applies to all sectors)
@param key_b Crypto1 Key B (applies to all sectors)
@warning This operation is irreversible
@@ -436,9 +441,19 @@ public:
bool mifarePlusUpgradeSecurityLevel1(
const m5::nfc::a::mifare::plus::AESKey& card_config_key = m5::nfc::a::mifare::plus::DEFAULT_KEY,
const m5::nfc::a::mifare::plus::AESKey& card_master_key = m5::nfc::a::mifare::plus::DEFAULT_KEY,
const m5::nfc::a::mifare::plus::AESKey& l2_switch_key = m5::nfc::a::mifare::plus::DEFAULT_KEY,
const m5::nfc::a::mifare::plus::AESKey& l3_switch_key = m5::nfc::a::mifare::plus::DEFAULT_KEY,
const m5::nfc::a::mifare::plus::AESKey& aes_sector_key = m5::nfc::a::mifare::plus::DEFAULT_FF_KEY,
const m5::nfc::a::mifare::classic::Key& key_a = m5::nfc::a::mifare::classic::DEFAULT_KEY,
const m5::nfc::a::mifare::classic::Key& key_b = m5::nfc::a::mifare::classic::DEFAULT_KEY);
/*!
@brief Upgrade security level to SL2 (AES over CRYPTO1)
@param sl2_switch_key SL2 Switch Key (AES)
@warning This operation is irreversible
@note Only supported on MIFARE Plus X / EV2
*/
bool mifarePlusUpgradeSecurityLevel2(
const m5::nfc::a::mifare::plus::AESKey& sl2_switch_key = m5::nfc::a::mifare::plus::DEFAULT_KEY);
///@}
@@ -540,7 +555,14 @@ protected:
bool mifare_get_version_L3(uint8_t ver[8]);
bool mifare_get_version_L4_raw(uint8_t* ver, uint16_t& ver_len);
bool mifare_get_version_L4_wrapped(uint8_t* ver, uint16_t& ver_len);
bool mifare_plus_authenticateAES(const uint16_t key_no, const m5::nfc::a::mifare::plus::AESKey& key);
bool mifare_plus_authenticateAES_L3(const uint16_t key_no, const m5::nfc::a::mifare::plus::AESKey& key);
bool mifare_plus_read_plain_nomac(const uint16_t block, const uint8_t count, std::vector<uint8_t>& out);
bool mifare_plus_read_plain_mac(const uint16_t block, const uint8_t count, std::vector<uint8_t>& out);
bool mifare_classic_value_block(const m5::nfc::a::Command cmd, const uint8_t block, const uint32_t arg = 0);
bool mifare_ultralightC_authenticate1(uint8_t ek[8]);
bool mifare_ultralightC_authenticate2(uint8_t rx_ek[8], const uint8_t tx_ek[16]);
@@ -551,6 +573,7 @@ protected:
bool dump_sector(const uint8_t sector);
bool dump_page_structure(const uint16_t maxPage);
bool dump_page(const uint8_t page, const uint16_t maxPage);
bool dump_mifare_plus_sl2(const m5::nfc::a::mifare::plus::AESKey& key);
bool dump_desfire();
bool dump_desfire_light();
@@ -562,6 +585,19 @@ protected:
m5::nfc::isodep::IsoDEP _isoDEP;
private:
bool mifare_plus_transceive_raw(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len);
struct MifarePlusSession {
bool authenticated{};
uint16_t key_no{};
uint16_t r_ctr{};
uint16_t w_ctr{};
uint8_t frame_num{};
std::array<uint8_t, 4> ti{};
std::array<uint8_t, 16> kmac{};
};
MifarePlusSession _mfp_session{};
std::unique_ptr<Adapter> _impl;
};