#11 NFC処理実装 動作確認
前回まででNFCカードにNDEFデータを読み書きする処理を実装しました。
今回は実際に動作させ、どのようなデータが返ってくるか見ていきます。
|
【注意事項】 管理者の許可なくデータの書き込みや改変を行う行為は、不正アクセス禁止法や刑法等の法令に違反する可能性があります。 |
・スマートフォンやICカードとの連携機能を実装したいエンジニア
・IoT機器や非接触決済の技術に関心のある開発者
開発環境
- OS: macOS Sonoma 14.5
- Xcode: Xcode 15.4 (15F31d)
事前準備
ドライバアンインストール
以前の記事同様に、ドライバがインストール済の場合はアンインストールします。
NFCカードの事前書き込み
別のツールを使って事前にカードにNDEFデータを書き込んでおきます。
今回は WakDev 社の「NFC Tools」を利用してNDEFデータを書き込みました。

今回の動作確認では、 test data という文字列レコード1つのみを書き込みました。
|
🔍補足 一度も書き込まれていないカードでもアクセスできるようにする方法は今後も調査してまいります。 |
モジュールとカードの設置
カードの捕捉は、プロトコル切り替えコマンドを実行する瞬間にのみ実施されます。
このため、今回の動作確認では、 RC-S660/S のアンテナ部にカードを置き、確実に反応するようにして動作確認します。

動作確認
それではビルドして動作確認してみましょう。
デバイスが Mac に接続されている場合、アプリを実行する前にデバイスを抜いておきます。
以前の記事 と同様に、ドライバインストール後、コンソールをアプリを開いてからデバイスを接続します。
Start メソッドにカードアクセス処理も実装しているため、USBを接続した時点でカードアクセス処理も実行されます。
それぞれの処理で受信した応答データを詳しく見ていきましょう。
なお、コマンドパケットについては前回までの記事で確認済、レスポンスパケットはAPDUレスポンス部以外はほぼ同様なので、APDUレスポンス部のみに着目して確認していきます。
カード操作のためのコマンドのレスポンス構造
カード操作のために使用する Manage Session , Switch Protocol , Transparent Exchange コマンドは、レスポンスデータが Data Field BER-TLV という構造になっています。
Data Field BER-TLV は、TLV構造データに加え、各コマンドで定義されるTLV構造のData objectが含まれる場合があります。
| TLV | 設定データ |
| Tag | C0h : Generic error status |
| Length | 03h |
| Value | エラーステータス(取り得るデータの詳細はコマンドリファレンス参照) |
| Data object | TLV構造のデータ(コマンドによって追加される) |
なお、通常のレスポンス構造同様、 Data Field BER-TLV の後にステータスバイト SW1, SW2 もつきます。
1. Transparent Session 開始

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | ステータスバイト | 90 00 |
ステータスバイト SW1 = 90 , SW2 = 00 は正常終了であり、本コマンドが正しく実行できた事を表します。
Value: エラーステータス の 00 90 00 も正常終了を表すため、 Transparent Session が正常に開始されたことを示します。
2. プロトコル切り替え

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | Data object | Tag | 5f 51 : ATR |
| 7 | Length | 06 | |
| 8 | Value: ATR | 3b 81 80 01 80 80 | |
| 14 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータスが共に正常終了のため、プロトコル切り替えが正常に実行されたことを示します。
Data object の ATR データは、 Type A/B の Layer4 指定時に付加されます。
3. フラグ設定

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータスが共に正常終了のため、フラグ設定が正常に実行されたことを示します。
4. RF 出力 ON

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータスが共に正常終了のため、 RF 出力が ON されたことを示します。
5. カードコマンド送受信
(1) SELECT : NDEF アプリケーション

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | Data object 1 | Tag | 92 : 受信データ最終バイト有効ビット数 |
| 6 | Length | 01 | |
| 7 | Value: 受信データ最終バイト有効ビット数 | 00 | |
| 8 | Data object 2 | Tag | 96 : 受信ステータス |
| 9 | Length | 02 | |
| 10 | Value: 受信ステータス | 00 00 | |
| 12 | Data object 3 | Tag | 97 : 受信データ |
| 13 | Length | 02 | |
| 14 | Value: 受信データ | 90 00 | |
| 16 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータスが共に正常終了のため、カードコマンドが正常に実行されたことを示します。
カードコマンド送受信で使用する Transceive (Tag= 95h ) 1つずつに対し、受信データ最終バイト有効ビット数 (Tag: 92h ), 受信ステータス (Tag: 96h ), 受信データ (Tag: 97h ) の3つがレスポンスとして返ります。

受信データに着目すると 90 00 が返っています。
受信データは送信時の SELECT コマンドのレスポンスであり、 SELECT コマンドもAPDUのため、これは SELECT コマンドに対するステータスバイト SW1, SW2 となります。
そして、ステータスバイト SW1 = 90, SW2 = 00 は正常終了であるため、 NDEF アプリケーションが正常に選択できた事を表します。
(2) SELECT : CC ファイル


| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | Data object 1 | Tag | 92 : 受信データ最終バイト有効ビット数 |
| 6 | Length | 01 | |
| 7 | Value: 受信データ最終バイト有効ビット数 | 00 | |
| 8 | Data object 2 | Tag | 96 : 受信ステータス |
| 9 | Length | 02 | |
| 10 | Value: 受信ステータス | 00 00 | |
| 12 | Data object 3 | Tag | 97 : 受信データ |
| 13 | Length | 02 | |
| 14 | Value: 受信データ | 90 00 | |
| 16 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータス、受信データ中のステータスバイト、全て正常終了のため、CCファイルが正常に選択できた事を表します。
(3) READ BINARY : NDEF ファイル制御 TLV データの読み出し

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | Data object 1 | Tag | 92 : 受信データ最終バイト有効ビット数 |
| 6 | Length | 01 | |
| 7 | Value: 受信データ最終バイト有効ビット数 | 00 | |
| 8 | Data object 2 | Tag | 96 : 受信ステータス |
| 9 | Length | 02 | |
| 10 | Value: 受信ステータス | 00 00 | |
| 12 | Data object 3 | Tag | 97 : 受信データ |
| 13 | Length | 11 | |
| 14 | Value: 受信データ | 00 11 20 00 3b 00 34 04 06 e1 04 1e 00 00 00 90 00 | |
| 31 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータスが共に正常終了のため、カードコマンドが正常に実行されたことを示します。

これまで同様、受信データの末尾の2バイトはステータスバイトです。
READ BINARY コマンドでは現在選択しているファイルのデータを読み出します。ステータスバイトの前に、コマンドパケットの Le で指定したサイズ分のデータが返却されます。
ここでは CC ファイルの先頭15バイトを読み込んで NDEF ファイル制御 TLV データを取得し、NDEFファイルのファイルIDを特定します。
なお、CCファイルのデータ構成の詳細は前回記事を参照ください。
(4) SELECT : NDEF ファイル


| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | Data object 1 | Tag | 92 : 受信データ最終バイト有効ビット数 |
| 6 | Length | 01 | |
| 7 | Value: 受信データ最終バイト有効ビット数 | 00 | |
| 8 | Data object 2 | Tag | 96 : 受信ステータス |
| 9 | Length | 02 | |
| 10 | Value: 受信ステータス | 00 00 | |
| 12 | Data object 3 | Tag | 97 : 受信データ |
| 13 | Length | 02 | |
| 14 | Value: 受信データ | 90 00 | |
| 16 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータス、受信データ中のステータスバイト、全て正常終了のため、NDEFファイルが正常に選択できた事を表します。
(5)a. READ BINARY : データ読み出し

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | Data object 1 | Tag | 92 : 受信データ最終バイト有効ビット数 |
| 6 | Length | 01 | |
| 7 | Value: 受信データ最終バイト有効ビット数 | 00 | |
| 8 | Data object 2 | Tag | 96 : 受信ステータス |
| 9 | Length | 02 | |
| 10 | Value: 受信ステータス | 00 00 | |
| 12 | Data object 3 | Tag | 97 : 受信データ |
| 13 | Length | 04 | |
| 14 | Value: 受信データ | 00 10 90 00 | |
| 18 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータスが共に正常終了のため、カードコマンドが正常に実行されたことを示します。

受信データには、 NDEF ファイルの先頭2バイトとステータスバイト2バイトが返却されます。
ステータスバイト 90 00 は正常完了を示すため、読み出しが正常完了したことを表します。
NDEF ファイルの先頭2バイトは、続くNDEFメッセージのサイズを示しており、次のデータ読み込みのコマンドパケットに利用します。

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | Data object 1 | Tag | 92 : 受信データ最終バイト有効ビット数 |
| 6 | Length | 01 | |
| 7 | Value: 受信データ最終バイト有効ビット数 | 00 | |
| 8 | Data object 2 | Tag | 96 : 受信ステータス |
| 9 | Length | 02 | |
| 10 | Value: 受信ステータス | 00 00 | |
| 12 | Data object 3 | Tag | 97 : 受信データ |
| 13 | Length | 12 | |
| 14 | Value: 受信データ | d1 01 0c 54 02 65 6e 74 65 73 74 20 64 61 74 61 90 00 |
|
| 32 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータスが共に正常終了のため、カードコマンドが正常に実行されたことを示します。

受信データには、NDEFメッセージとステータスコード2バイトが返却されます。
ステータスバイト 90 00 は正常完了を示すため、読み出しが正常完了したことを表します。
NDEFメッセージはNDEFレコードの集合体ですので、前回の記事で紹介したNDEFレコードの形式に則って確認します。
| オフセット | 名称 | 実データ | |
| 0 | MB, ME, CF, SR, IL, TNF | d1 = 11010001b | |
| 1 | TYPE_LENGTH | 01 | |
| 2 | PAYLOAD_LENGTH | 0c | |
| 3 | TYPE | 54 : 文字列レコード | |
| 4 | PAYLOAD | エンコード、言語コード長 | 02 = 00000010b |
| 5 | 言語コード | 65 6e | |
| 7 | 文字列データ | 74 65 73 74 20 64 61 74 61 |
MB , ME に共に 1 がセットされているため、単一のレコードで構成されたNDEFメッセージであることを示しています。
また、 SR に 1 がセットされているため PAYLOAD_LENGTH は1バイト、 IL は 0 なので ID 及び ID_LENGTH が存在しないことを示しています。
TNF の値は 1 なので、 TYPE には NFC Forum で規定されているレコードタイプ定義(RTD)が指定されていることを示します。
TYPE の値 54h は、 NFC RTDにおいて文字列レコードを示します。
続いて PAYLOAD を見ていきましょう。
1バイト目の1ビット目はエンコードを表し、 0 の場合は UTF-8 であることを示します。
末尾6ビットは言語コードのサイズを表し、この後2バイトが言語コードであることを示します。
言語コード 65 6e は、 UTF-8 では en となります。
このため、この後に続く文字列データの言語が英語であることを示します。
文字列データ 74 65 73 74 20 64 61 74 61 は、 UTF-8 では test data となります。
以上、実際に読み込んだデータが、事前準備として書き込んだNDEFデータと一致していることが確認できました。
(5)b. UPDATE BINARY : データ書き込み


| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | Data object 1 | Tag | 92 : 受信データ最終バイト有効ビット数 |
| 6 | Length | 01 | |
| 7 | Value: 受信データ最終バイト有効ビット数 | 00 | |
| 8 | Data object 2 | Tag | 96 : 受信ステータス |
| 9 | Length | 02 | |
| 10 | Value: 受信ステータス | 00 00 | |
| 12 | Data object 3 | Tag | 97 : 受信データ |
| 13 | Length | 02 | |
| 14 | Value: 受信データ | 90 00 | |
| 16 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータス、受信データ中のステータスバイト、全て正常終了のため、データが正常に書き込めた事を表します。

実際に別のツールでカードのデータを読み込むと、コマンドパケットで指定した文字列 write test が設定されていることがわかります。

また、該当レコードの詳細を見ると、コマンドパケットで指定した各データ通りのレコードとなっていることがわかります。
6. RF 出力 OFF

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータスが共に正常終了のため、 RF 出力が OFF されたことを示します。
7. Transparent Session 終了

| オフセット | 名称 | 実データ | |
| 0 | Data Field BER-TLV | Tag | c0 : Generic error status |
| 1 | Length | 03 | |
| 2 | Value: エラーステータス | 00 90 00 | |
| 5 | ステータスバイト | 90 00 |
ステータスバイト、エラーステータスが共に正常終了のため、 Transparent Session が終了されたことを示します。
おわりに
以上で本シリーズにおける Mac 向け NFC 通信ドライバ開発は完了となります。
今回はNFC通信のための基本的な内容となりましたが、ユーザー操作画面との連携やさまざまなカードへの対応など、実製品としてはまだまだ考慮すべき点が多々あります。
今後も調査研究・開発を重ねていきます。

