開発

NFC#09 NFC処理実装[前編] – カードアクセス

NFC#09 NFC処理実装[前編] - カードアクセス

今回は開発用のブランクカードを使い、ICカード内のデータに対するデータ読み書きを実装します。

【注意事項】
本記事は、NFC技術の一般的な仕組みを解説するものです。
公共機関や第三者が管理・運用するNFCタグやICカードへの無断での書き込み・改変を推奨または容認するものではありません。

管理者の許可なくデータの書き込みや改変を行う行為は、不正アクセス禁止法や刑法等の法令に違反する可能性があります。
これらの行為により生じたいかなる損害・法的責任についても、当方は一切の責任を負いません。

・NFCを活用した新しいサービスや製品を企画・開発したい方
・スマートフォンやICカードとの連携機能を実装したいエンジニア
・IoT機器や非接触決済の技術に関心のある開発者

完成品画像NFC完成品紹介ページはこちら

開発環境

  • OS: macOS Sonoma 14.5
  • Xcode: Xcode 15.4 (15F31d)

 

使用するICカード

今回使用するカードは、何もデータが書き込まれていないブランクカードを使用します。
なお、開発で使用するカードには目印としてシールを貼っております。

また、このカードは ISO/IEC 14443-4 Type A に準拠、及び NFC フォーラム タイプ4タグ に対応しております。

注)
カードが準拠している通信インターフェース、タグのタイプは、のちの通信設定のために重要です。

 

代表的な通信手順

カードと通信するための代表的なシーケンス、及びAPDUコマンドの対応は下表の通りです。

手順 操作 対応コマンド CLA INS P1 P2
1 Transparent Session 開始 Manage Session FFh C2h 00h 00h
2 プロトコル切り替え Switch Protocol FFh C2h 00h 02h
3 フラグ設定(カードコマンドに依存) Transparent Exchange FFh C2h 00h 01h
4 RF 出力 ON Manage Session FFh C2h 00h 00h
5 カードコマンド送受信 Transparent Exchange FFh C2h 00h 01h
6 RF 出力 OFF Manage Session FFh C2h 00h 00h
7 Transparent Session 終了 Manage Session FFh C2h 00h 00h

ここで、異なる操作で同一のコマンドが使用されますが、コマンドデータの内容に応じて各操作が実現されます。
また、コマンドデータは前回の記事で紹介したTLV形式で設定されます。

 

各操作を実現するためのコマンドパケット

それでは実装を進めていきます。
実装と言っても、データが変わるだけで送信・受信処理はほとんど変わらないため、本記事ではコマンドパケットのみに注目して解説していきます。

なお、今回はプログラムが複雑にならないよう、 Start メソッド内(前回記事のバージョン取得の直後)に実装していきます。

Start メソッドはドライバ起動時の処理を実装するため、本来であれば Start メソッドにカードアクセス処理を実行するのは好ましくありません。(製品の仕様によります)
しかし、画面UIからユーザーが任意のタイミングで実行できるようにする場合の実装量が増え、本題のNFC体験まで遠くなってしまうため、今回は単純化のために Start メソッドに実装いたします。

 1. Transparent Session 開始


uint8_t sendStartTransparentBuffer[] = {
    0x00, 0x00, 0xff,       // プリアンブル、スタートコード
    0x00, 0x11, 0xef,       // データ長、チェックサム
                            // パケットデータ : CCIDコマンド
    0x6b,                   //   メッセージタイプ : PC_to_RDR_Escape
    0x07, 0x00, 0x00, 0x00, //   データ長
    0x00, 0x01,             //   スロット番号、シーケンス番号
    0x00, 0x00, 0x00,       //   固有ヘッダー
                            //   固有データ : APDUコマンド
    0xff, 0xc2, 0x00, 0x00, //     CLA, INS, P1, P2
    0x02,                   //     Lc
                            //     Data In
    0x81,                   //       Tag : Start Transparent Session
    0x00,                   //       Length
    0x49,                   // データチェックサム
    0x00                    // ポストアンブル
};

カードと通信するためには、まず Transparent Session を開始して RC-S660/S を Transparent 状態にする必要があります。
以降の操作で使用する一部のコマンドは、 Transparent 状態ではない時は使用不可であり、実行するとエラーが返却されます。

Transparent Session 開始のためのコマンドデータは以下となります。

TLV 設定データ
Tag 81h : Start Transparent Session
Length 00h
Value なし

 

 2. プロトコル切り替え


uint8_t sendSwitchProtocolBuffer[] = {
    0x00, 0x00, 0xff,       // プリアンブル、スタートコード
    0x00, 0x13, 0xed,       // データ長、チェックサム
                            // パケットデータ : CCIDコマンド
    0x6b,                   //   メッセージタイプ : PC_to_RDR_Escape
    0x09, 0x00, 0x00, 0x00, //   データ長
    0x00, 0x02,             //   スロット番号、シーケンス番号
    0x00, 0x00, 0x00,       //   固有ヘッダー
                            //   固有データ : APDUコマンド
    0xff, 0xc2, 0x00, 0x02, //     CLA, INS, P1, P2
    0x04,                   //     Lc
                            //     Data In
    0x8f,                   //       Tag : Switch Protocol Data Object
    0x02,                   //       Length
    0x00, 0x04,             //       Value : Standard type, Layer
    0x2e,                   // データチェックサム
    0x00                    // ポストアンブル
};

次に Transparent Session で使用する規格を指定してカードを捕捉します。
NFC は準拠する規格に応じて、 Type A や Type F というように区別されます。
ここで設定する規格と、タッチされたカードが準拠する規格が適合していれば、 RC-S660/S とカード間でNFC通信が可能となります。

プロトコル切り替えのためのコマンドデータは以下となります。

TLV 設定データ
Tag 8Fh : Switch Protocol Data Object
Length 02h
Value 1バイト目:規格
2バイト目:レイヤー

今回使用するカードは ISO/IEC 14443-4 Type A に準拠してるので、1バイト目の規格には Type A を表す 00h を設定します。
また、レイヤーはカードの捕捉まで実行する 04h を設定します。

カードが準拠する規格によって規格・レイヤーに設定する値が変わるため、カードが準拠している規格を確認の上、公式リファレンスに従って設定する必要があります。

カードの捕捉は、本コマンドを実行する瞬間にのみ実施されます。
すなわち、本コマンドを実行後にカードをタッチしても、うまくカードを捕捉できない場合があります。
このため、実製品に適用する場合、繰り返し本コマンドを実行するなどの工夫が必要です。

 

 3. フラグ設定(カードコマンドに依存)


uint8_t sendTransmissionReceptionFlagBuffer[] = {
    0x00, 0x00, 0xff,       // プリアンブル、スタートコード
    0x00, 0x13, 0xed,       // データ長、チェックサム
                            // パケットデータ : CCIDコマンド
    0x6b,                   //   メッセージタイプ : PC_to_RDR_Escape
    0x09, 0x00, 0x00, 0x00, //   データ長
    0x00, 0x03,             //   スロット番号、シーケンス番号
    0x00, 0x00, 0x00,       //   固有ヘッダー
                            //   固有データ : APDUコマンド
    0xff, 0xc2, 0x00, 0x01, //     CLA, INS, P1, P2
    0x04,                   //     Lc
                            //     Data In
    0x90,                   //       Tag : Transmission and Reception Flag
    0x02,                   //       Length
    0x00, 0x00,             //       Value : 送受信処理フラグ
    0x31,                   // データチェックサム
    0x00                    // ポストアンブル
};

カードコマンドに応じて送受信に関する挙動を設定します。
今回は送受信処理フラグ設定のみ実施します。

送受信処理フラグ設定のためのコマンドデータは以下となります。

TLV 設定データ
Tag 90h : Transmission and Reception Flag
Length 02h
Value 送受信処理フラグ

送受信処理フラグでは、 Protocol Prologue(ISO/IEC 14443-4 の PCB, CID, NAD) や CRC, パリティの自動処理の挙動を設定できます。
今回は全て自動処理する設定として 0000h を設定します。

 

 4. RF 出力 ON


uint8_t sendRfOnBuffer[] = {
    0x00, 0x00, 0xff,       // プリアンブル、スタートコード
    0x00, 0x11, 0xef,       // データ長、チェックサム
                            // パケットデータ : CCIDコマンド
    0x6b,                   //   メッセージタイプ : PC_to_RDR_Escape
    0x07, 0x00, 0x00, 0x00, //   データ長
    0x00, 0x04,             //   スロット番号、シーケンス番号
    0x00, 0x00, 0x00,       //   固有ヘッダー
                            //   固有データ : APDUコマンド
    0xff, 0xc2, 0x00, 0x00, //     CLA, INS, P1, P2
    0x02,                   //     Lc
                            //     Data In
    0x84,                   //       Tag : Turn On RF Field
    0x00,                   //       Length
    0x43,                   // データチェックサム
    0x00                    // ポストアンブル
};

送受信の準備ができたらRF出力をオンにします。
NFCは電波を利用して無線通信します。RF出力をオンにすることで通信のための電波を発生させます。

RF出力オンのためのコマンドデータは以下となります。

TLV 設定データ
Tag 84h : Turn On RF Field
Length 00h
Value なし

 

 5. カードコマンド送受信

RF出力をオンにしたらカードと任意のデータを送受信することできるようになります。
カードが準拠する仕様に則ってデータを送信することで、 RC-S660/S からカード内のデータを操作することができます。

今回使用するカードは NFC フォーラム タイプ4タグ に準拠しています。
この仕様に則ると、複数回のデータ通信が必要となります。
そのため、本記事ではカードコマンドを除いた基本となるパケット構造のみとし、次回の記事で詳細なカードコマンドについて実装していきます。


// 基本となる送受信コマンドパケットデータ
uint8_t sendTxRxBuffer[] = {
    0x00, 0x00, 0xff,       // プリアンブル、スタートコード
    0x00, 0xXX, 0xXX,       // データ長、チェックサム(それぞれカードコマンドによる)
                            // パケットデータ : CCIDコマンド
    0x6b,                   //   メッセージタイプ : PC_to_RDR_Escape
    0xXX, 0xXX, 0xXX, 0xXX, //   データ長(カードコマンドによる)
    0x00, 0xXX,             //   スロット番号、シーケンス番号(コマンド送信ごとに増加)
    0x00, 0x00, 0x00,       //   固有ヘッダー
                            //   固有データ : APDUコマンド
    0xff, 0xc2, 0x00, 0x01, //     CLA, INS, P1, P2
    0xXX,                   //     Lc(カードコマンドによる)
                            //     Data In 1
    0x5f, 0x46,             //       Tag : Timer
    0x04,                   //       Length
    0x60, 0xea, 0x00, 0x00, //       Value : 受信待ちタイムアウト時間(マイクロ秒単位)
                            //     Data In 2
    0x95,                   //       Tag : Transceive (Transmit and Receive)
    0xXX,                   //       Length(カードコマンドによる)
    0xXX, 0xXX, 0xXX, ...,  //       Value : カードコマンドデータ(カードコマンドによる)
    0xXX,                   // データチェックサム(カードコマンドによる)
    0x00                    // ポストアンブル
};

ここで、APDUコマンドのコマンドデータ(Data In)に、2つのTLV形式データを設定しています。
このようにコマンドデータ(Data In)には、必要に応じて複数のTLV形式データを設定することができます。
今回は、データ送受信前に、受信待ちタイムアウト時間を設定しています。

受信待ちタイムアウト時間設定のためのコマンドデータは以下となります。

TLV 設定データ
Tag 5f46h : Timer
Length 04h
Value タイムアウト時間(マイクロ秒単位、リトルエンディアン)

また、データ送受信のためのコマンドデータは以下となります。

TLV 設定データ
Tag 95h : Transceive (Transmit and Receive)
Length 送信データサイズ
Value 送信データバイト列

 

 6. RF 出力 OFF


uint8_t sendRfOffBuffer[] = {
    0x00, 0x00, 0xff,       // プリアンブル、スタートコード
    0x00, 0x11, 0xef,       // データ長、チェックサム
                            // パケットデータ : CCIDコマンド
    0x6b,                   //   メッセージタイプ : PC_to_RDR_Escape
    0x07, 0x00, 0x00, 0x00, //   データ長
    0x00, 0x0c,             //   スロット番号、シーケンス番号
    0x00, 0x00, 0x00,       //   固有ヘッダー
                            //   固有データ : APDUコマンド
    0xff, 0xc2, 0x00, 0x00, //     CLA, INS, P1, P2
    0x02,                   //     Lc
                            //     Data In
    0x83,                   //       Tag : Turn Off RF Field
    0x00,                   //       Length
    0x3c,                   // データチェックサム
    0x00                    // ポストアンブル
};

カードコマンドの送受信完了後、RF出力をオフにします。
RF出力オフのためのコマンドデータは以下となります。

TLV 設定データ
Tag 83h : Turn Off RF Field
Length 00h
Value なし

 

 7. Transparent Session 終了


uint8_t sendEndTransparentBuffer[] = {
    0x00, 0x00, 0xff,       // プリアンブル、スタートコード
    0x00, 0x11, 0xef,       // データ長、チェックサム
                            // パケットデータ : CCIDコマンド
    0x6b,                   //   メッセージタイプ : PC_to_RDR_Escape
    0x07, 0x00, 0x00, 0x00, //   データ長
    0x00, 0x0d,             //   スロット番号、シーケンス番号
    0x00, 0x00, 0x00,       //   固有ヘッダー
                            //   固有データ : APDUコマンド
    0xff, 0xc2, 0x00, 0x00, //     CLA, INS, P1, P2
    0x02,                   //     Lc
                            //     Data In
    0x82,                   //       Tag : End Transparent Session
    0x00,                   //       Length
    0x3c,                   // データチェックサム
    0x00                    // ポストアンブル
};

最後に Transparent Session を終了して通常状態に戻し、NFC通信の一連の処理が完了となります。
Transparent Session 終了のためのコマンドデータは以下となります。

TLV 設定データ
Tag 82h : End Transparent Session
Length 00h
Value なし

 

おわりに

今回はNFCカードアクセスのための大まかな流れについて実装しました。
次回は本題である、NFCカード読み書きのためのデータ送受信について実装していきます。

 


 

TOP