(Felica/Mifare/NFC チャレンジシリーズ) その他の記事はこちら 「Felica/Mifare/NFC でいろいろ実験」
https://qiita.com/nanbuwks/items/1f416d6e45a87250ee0a
Raspberry Pi で libnfc + PN532 NFC RFID module
https://qiita.com/nanbuwks/items/0c73add503b354774035
にて libnfc を試してみました。これを元に PN532 NFC RFID module を操作するプログラムをかけばいいのですが、なかなか見通しが悪くサンプルプログラム以上のことをしようとしても難しい。
さて、examples の中に pn53x-tamashell というファイルがあり、これは pn53x に直接命令を送れるのかな?
公式のドキュメントなどは見つけることができなかったが、アイスランドの教材らしい pdf が見つかった。
Mifare Ultralight に対してポーリング/読み込み/書き込み/認証/盗聴/エミュレーションを行っています。
ポーリング
pdf で書かれているポーリングのレッスンを試してみます。
コマンド
> 4a0100
と入力すると以下のように待受になります。
4a0100
Tx: 4a 01 00
Mifare Classic 1K を近づけると以下のように反応がありました。
Rx: 01 01 00 04 08 04 c5 b2 0f ad
コマンドの詳細はユーザマニュアル
https://www.nxp.com/docs/en/user-guide/141520.pdf
に記されています。
InListPassiveTarget コマンドとして、4Aを送出、
ISO/IEC14443 Type A (Mifare) を指定しています。
返事として以下が返ってきていることがわかります。
- NbTg 01
- Tg 01
- SENS_RES 00 04
- SEL_RES 08
- NFCIDLength 04
- NFCID1 C5 B2 0F AD
AutoColl/AutoPoll
先のコマンドでは FeliCa の検出はできませんでした。FeliCa と Mifare両方対応するために、
「PN532 NFC RFID module + Arduino で FeliCa と Mifare を判別する」
https://qiita.com/nanbuwks/items/a264201dcc0b4e12e72d
の AutoColl/AutoPoll コマンドを試してみます。
何もない時
6001011011
Tx: 60 01 01 10 11
Rx: 00
AutoPoll に Mifair カード が検出された場合
> 6001011011
6001011011
Tx: 60 01 01 10 11
Rx: 01 10 09 01 00 04 08 04 c5 b2 0f ad
01 | tg枚 |
10 | カードType:MiFare |
09 | データ長 |
01 | 論理番号 |
00 04 | ATQA (SENS_RES) |
08 | SAK (SEL_RES) |
04 | NFCID1 長 |
c5 b2 0f ad | UID (NFCID1) |
のカードが検出できています。
AutoPoll に FeliCa カード が検出された場合
> 6001011011
6001011011
Tx: 60 01 01 10 11
Rx: 01 11 13 01 12 01 01 14 b4 27 6b 11 5a 23 0f 0d 23 04 2f 77 83 ff
>
01 | Tg枚 |
11 | カードType:FeliCa |
13 | データ長 |
01 | 論理番号 |
12 | POL_RES |
01 | Response Code |
01 14 b4 27 6b 11 5a 23 | NFCID2t |
0f 0d 23 04 2f 77 83 ff | Parameter (PAD) |
?? Pad って何かな?
以下のようにすると無限に待ち受けに入ります
60FF011011
Tx: 60 ff 01 10 11
カードを近づけると返事が返ってきます。
Rx: 01 10 09 01 00 04 08 04 c5 b2 0f ad
上記はMifareに反応した場合の返答です。先の返答と同じ値が返ってきてますね。
GetFirmwareVersion
02
Tx: 02
Rx: 32 01 06 07
32 | IC:PN532 |
01 | Ver. 1 |
06 | Rev. 6 |
07 | Support ISO18092,ISO/IEC14443 TypeB,ISO/IEC14443 TypeA |
データ読んでみる
InDataExchange かな?
この通り、送ってみます。
最初に鍵を送っておきます。
> 40016004feeffeeffeeff2f3f4f5
40016004feeffeeffeeff2f3f4f5
Tx: 40 01 60 04 fe ef fe ef fe ef f2 f3 f4 f5
Rx: 00
次に
> 40013004
40013004
Tx: 40 01 30 04
Rx: RF Transmission Error
うまくいきません
「ハードウェアハッキングして NFC コントローラの命令を解析する」
https://qiita.com/nanbuwks/items/8baa0408e08812258c95
のようにして、必要なコマンドを調べてみました。
InDataExchange の前に InListPassiveTarget が必要っぽかったので、以下のようにしてみました。
> 4A0100
4A0100
Tx: 4a 01 00
Rx: 01 01 00 04 08 04 f2 f3 f4 f5
このあと鍵 FEEFFEEFFEEFを送ります
> 40016004feeffeeffeeff2f3f4f5
40016004feeffeeffeeff2f3f4f5
Tx: 40 01 60 04 fe ef fe ef fe ef f2 f3 f4 f5
Rx: 00
セクタ4を読んでみます
> 40 01 30 04
40 01 30 04
Tx: 40 01 30 04
Rx: 00 33 30 31 32 33 34 35 36 37 38 00 00 00 00 00 00
読めました
このRXの中身
00 | Status=0x00: terminated successfully |
33 30 31 32 33 34 35 36 37 38 00 00 00 00 00 00 | DataIn |
DataIn はセクターのデータになります
良く見ると、InDataExchangeの前にInListPassiveTargetを送っている図がありました。
ここでTarget numberを取得しないといけない
なお、一連の操作はタイミングを早くしないとうまくいかなかったです。
カードを置かずに
4A0100
40016004feeffeeffeeff2f3f4f5
40 01 30 04
を連続でコマンド投入して、それからカードを置くと3つ一緒にうまく処理できました。
初期化
32 05 FF 01 FF : RFConfiguration MaxRetries ( MxRtyATR=FF, MxRtyPSL=01, MxRtyPassiveActivation=FF )
14 01 14 01 : SAMConfiguration Mode=Normal mode,TIMEOUT=14, IRQ=driven
4A 01 00 : InListPassiveTarget MaxTg=1,BrTy=106 kbps type A (ISO/IEC14443 Type A)
60 01 01 11 10 : InAutoPoll PollNr =1,Period=1, Type1=0x11 ,Type2=0x10
4A 01 00 E1 00 : InListPassiveTarget MaxTg=1,BrTy=106 kbps type A (ISO/IEC14443 Type A)
40 01 14 06 011606000a1e7703 01 8B 1A 03 80 00 80 02 80 03
スクリプト
見てみると、TAMASHELLはスクリプトエンジンを入っているとかそういうことではなく、単にリダイレクトでパラメータを入れているだけみたいです。
ReadNavigo.sh を元にしてみました。
Mifareを読む
上のものを押し込んでみました。
#!/bin/sh
ID=$(cat << EOF | \
pn53x-tamashell |\
grep -A1 "^Tx: 4a 01 00" |\
sed -e '1d' -e "s/^Rx: 01 .. .. .. .. .. \(.. .. .. ..\).*/\1/" -e 's/ //g'
# Timeouts
#3205000002
# ListTarget
4a0100
# TypeB' APGEN
#42010b3f80
EOF
)
if [ -z "$ID" ]; then
echo "Error: can not read ID" >&2
exit 1
fi
echo $ID
DATA=$(cat << EOF | \
pn53x-tamashell |\
grep -A1 "^Tx: 40 01 30" |\
sed -e '1d' -e "s/^Rx: 00 \(.. .. .. ..\).*/\1/" -e 's/ //g'
4A0100
40 01 60 04 FE EF FE EF FE EF $ID
40 01 30 04
EOF
)
if [ -z "$DATA" ]; then
echo "Error: Can not read sector 4 data" >&2
exit 1
fi
echo $DATA
実行
$ ReadMifare.sh
f26b7b3e
33303135
TAMASHELLでFeliCa読んでみる
Polling
> 4a 01 01 00 fe 00 01 00
4a 01 01 00 fe 00 01 00
Tx: 4a 01 01 00 fe 00 01 00
Rx: 01 01 14 01 11 16 06 00 0a 1e 77 03 03 32 42 82 82 47 aa ff fe 00
解読すると、
パケット内容 | 意味 |
---|---|
4A | 命令:InListPassiveTarget |
01 | MaxTg = 0x01 |
01 | BrTy = 0x01 : : 212 kbps (FeliCa polling) |
00 FE 00 01 00 | InitiatorData |
となり、InitiatorData は FeliCa のコマンドとなります。
FeliCaコマンドについては、「FeliCa Card User's Manual Excerpted Edition」
を見てみます。
https://www.sony.net/Products/felica/business/tech-support/
00 | Polling command |
FE00 | System Code |
01 | Request Code=0x01:System code request |
00 | Time Slot=0x00: Designation of maximum number of slots possible to respond=1 |
System Code FE00とは何かな?
https://japannfcreader.tret.jp/help-improve-the-app.html
や、
https://gist.github.com/oboenikui/ee9fb0cb07a6690c410b872f64345120
によると、楽天Edy nanaco WAON 大学生協ICプリペイドなどが該当している。
決済機能の付いたものが FE00 になるのかな?
返ってきた返答:
01 01 14 01 01 01 06 01 67 02 a5 15 03 32 42 82 82 47 aa ff fe 00
パケット内容 | 意味 |
---|---|
01 | NbTg |
01 | Logical number |
14 | length |
01 | response code byte |
11 16 06 00 0a 1e 77 03 | NFCID2t |
03 32 42 82 82 47 aa ff | PAD |
fe 00 | System code |
Read without Encryption
> 40 01 14 06 01 01 06 01 67 02 a5 15 01 8B 1A 03 80 00 80 02 80 03
40 01 14 06 01 01 06 01 67 02 a5 15 01 8B 1A 03 80 00 80 02 80 03
Tx: 40 01 14 06 01 01 06 01 67 02 a5 15 01 8b 1a 03 80 00 80 02 80 03
Rx: 00 0c 07 01 01 06 01 67 02 a5 15 01 a6
40 | InDataExchange |
01 | Tg=01 |
14 06 01 01 06 01 67 02 a5 15 01 8b 1a 03 80 00 80 02 80 03 | Felica DataOut |
Felica DataOutの中身
14 | Len |
06 | Cmd:Read without Encryption |
01 01 06 01 67 02 a5 15 | IDm |
01 | Number of Service |
8b 1A | Service Code List |
03 | Number of Block |
80 00 | Block List 1 |
80 02 | Block List 2 |
80 03 | Block List 3 |
Responce
00 | Status:successflly? |
0c | Len |
07 01 01 06 01 67 02 a5 15 01 a6 | DataIn |
DataIn の中身
07 | Responce Code |
01 01 06 01 67 02 a5 15 | Idm |
01 | Status Flag 1:最初のブロック処理でエラー発生 |
a6 | Status Flag 2:Illegal Service Code List |
うーん、エラーで読めてないですね
ここのところだけやり直して
Rx: 00 3d 07 01 01 06 01 67 02 a5 15 00 00 03 30 31 31 30 31 33 30 32 30 39 36 31 00 00 31 31 33 36 30 31 30 30 36 34 32 30 31 33 30 34 30 31 32 30 32 31 30 33 33 31 39 39 39 39 39 39 39 39
Responce
00 | Status:successflly? |
3d | ? |
07 01 01 06 01 67 02 a5 15 00 00 03 30 31 31 30 31 33 30 32 30 39 36 31 00 00 31 31 33 36 30 31 30 30 36 34 32 30 31 33 30 34 30 31 32 30 32 31 30 33 33 31 39 39 39 39 39 39 39 39 | DataIn |
DataIn の中身
07 | Responce Code |
01 01 06 01 67 02 a5 15 | Idm |
00 | Status Flag 1 |
00 | Status Flag 2 |
03 | Block num |
30 31 32 33 34 35 36 37 38 39 30 31 00 00 31 31 | Block1 :"012345678901\0\011" |
33 36 30 31 30 30 36 34 32 30 31 33 30 34 30 31 | Block2:"3601006420130401" |
32 30 32 32 30 33 33 31 39 39 39 39 39 39 39 39 | Block3:"20220331999999999" |
スクリプト
上のものを押し込んで
#!/bin/sh
ID=$(cat << EOF | \
pn53x-tamashell |\
grep -A1 "^Tx: 4a 01 01" |\
sed -e '1d' -e "s/^Rx: 01 .. .. .. \(.. .. .. .. .. .. .. ..\).*/\1/" -e 's/ //g'
# FeliCa polling
4a010100FE000100
EOF
)
if [ -z "$ID" ]; then
echo "Error: can not read ID" >&2
exit 1
fi
echo $ID
DATA=$(cat << EOF | \
pn53x-tamashell |\
grep -A1 "^Tx: 40 01 .. 06" |\
sed -e '1d' -e "s/^Rx: 00 .. 07 .. .. .. .. .. .. .. .. .. .. .. \(.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..\).*/\1/" -e 's/ //g'
# FeliCa polling
4a010100FE000100
# FeliCa read
40 01 14 06 $ID 01 8b 1a 03 80 00 80 02 80 03
EOF
)
if [ -z "$DATA" ]; then
echo "Error: Can not read sector 4 data" >&2
exit 1
fi
echo $DATA
FeliCa polling して、Idmを取得します。
取得したIdm を使って、 8b1a の 8000のブロックを読み込みます。
プログラムでは FeliCa polling で Idm を取得したら pn53x-tamashell が一旦終了して再度呼び出すことになってしまいます。そのときにセッションぽいものが切れてしまい、パラメータエラーとなるので、わざわざ 再度 FeliCa polling をムダにかけています。
また、内部では8000,8002,8003 の3つのブロックを読んでますが表示は8000のみを行っています。
感想
pn53x-tamashell を使うと、直接 pn532 とやりとりができます。
SPI/I2C/シリアル どれに対しても同じ操作でやりとりができます。
チップマニュアルとプロトコルマニュアルを見ながら直接パケットを書けるので、ライブラリの処理がチップのどの処理にあたるのかなどに悩まなくても良くなります。
しかしながら思ったより透過的ではなかったです。エラー時に、エラーパケットを返さずにメッセージだけしか出なかったりするので、はてなーと悩んでしまいます。
結局他の手法でエラーパケットの中身を調べて、マニュアルでエラー番号を参照することも必要になってきます。
結局命令バイト数やチェックサム、プリアンプルなどを計算したり付加したりするのが自動になっているだけのメリットしか無いような。