やったこと
RC-S300というカードリーダーを使ってMIFARE Classicの読み込みを行う例です。
必要なライブラリ
libnfcを使用します。Fedoraの場合以下コマンドでインストールできます。
dnf install libnfc-devel
実装
includeとか。マクロはデバッグに役立つので定義してます。
#include <iostream>
#include <string>
#include <sstream>
#include <boost/format.hpp>
#include <nfc/nfc.h>
#define S_ __FILE__ << ":" << __LINE__ << " "
#define MAX_FRAME_LEN 264
std::string hexdump(const uint8_t *p, size_t l) {
std::stringstream result;
result << l << "bytes\n";
while (l != 0) {
int i;
result << " ";
for (i = 0; i < 16; ++i) {
uint8_t value = *p++;
result << (boost::format(" %02X") % (uint32_t) value);
if (--l == 0) {
break;
}
}
result << "\n";
}
return result.str();
}
void dump(const uint8_t *buf, std::size_t s) {
std::cout << hexdump(buf, s);
return;
}
初期化とか
nfc_context *context;
nfc_device *device;
// NFCコンテキストの初期化
nfc_init(&context);
if (context == nullptr) {
std::cerr << S_ << "nfc_init error" << std::endl;
return 1;
}
device = nfc_open(context, NULL);
if (device == NULL) {
std::cerr << S_ << "nfc_open error" << std::endl;
nfc_exit(context);
return 1;
}
if (nfc_initiator_init(device) < 0) {
std::cerr << S_ << "nfc_initiato_init error" << std::endl;
nfc_close(device);
nfc_exit(context);
return 1;
}
UIDを読み取る
nfc_initiator_select_passive_target
の時点でカードをかざしていないとこの関数はすぐ成功で完了してしまいます。どうもpollingを行うnfc_initiator_poll_target
はこのカードリーダーでうまく動かないようです。
const nfc_modulation nm_mifare = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
nfc_target target;
if (nfc_initiator_select_passive_target(device, nm_mifare, NULL, 0, &target) <= 0) {
nfc_perror(device, "nfc_initiator_select_passive_target");
nfc_close(device);
nfc_exit(context);
return 1;
}
// UID読み取り
uint8_t abt_rx[MAX_FRAME_LEN];
int res = 0;
const uint8_t read_uid[] = {0xFF, 0xCA, 0x00, 0x00, 0x00};
if ((res = nfc_initiator_transceive_bytes(device, read_uid, sizeof(read_uid), abt_rx, sizeof(abt_rx), 0)) < 0) {
std::cerr << S_ << "error" << std::endl;
nfc_close(device);
nfc_exit(context);
return 1;
}
dump(abt_rx, res);
セクターのデータ読み出し
Load Authentication Keysコマンド解説にあるように、最初にキー情報のロードを行います。
const uint8_t buf[] = {0xFF, 0x82, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
if ((res = nfc_initiator_transceive_bytes(device, buf, sizeof(buf), abt_rx, sizeof(abt_rx), 0)) < 0) {
std::cerr << S_ << "error" << std::endl;
nfc_close(device);
nfc_exit(context);
return 1;
}
dump(abt_rx, res);
for (uint8_t first_block = 0x0; first_block < 64; first_block += 4) {
read_sector(device, first_block);
}
read_sector
とread_block
は以下のような実装です。
void read_block(nfc_device *device, uint8_t block) {
uint8_t abt_rx[MAX_FRAME_LEN];
int res = 0;
uint8_t read_block[] = {0xFF, 0xB0, 0x00, block, 0x10};
if ((res = nfc_initiator_transceive_bytes(device, read_block, sizeof(read_block), abt_rx, sizeof(abt_rx), 0)) < 0) {
std::cerr << S_ << "error" << std::endl;
return;
}
dump(abt_rx, res);
return;
}
void read_sector(nfc_device *device, const uint8_t first_block) {
uint8_t abt_rx[MAX_FRAME_LEN];
int res = 0;
uint8_t buf[] = {0xFF, 0x86, 0x00, 0x00, 0x05, 0x01, 0x00, first_block, 0x60, 0x00};
if ((res = nfc_initiator_transceive_bytes(device, buf, sizeof(buf), abt_rx, sizeof(abt_rx), 0)) < 0) {
std::cerr << S_ << "error" << std::endl;
return;
}
std::cout << "---- first_block: " << (uint32_t) first_block << std::endl;
dump(abt_rx, res);
for (uint8_t block = first_block; block < first_block + 4; ++block) {
read_block(device, block);
}
return;
}