MIFARE Classic 1K/4Kで電子財布が作れるValue blocks機能

MIFARE Classic 1K/4Kには、Value blocksという機能があり、Value blockは、電子財布を実行できるそうです。

面白そうなので、シナリオを考えてテストしてみることにしました。

テスト環境:
・ICカードリーダ:SONY RC-S380
・Visual Studio 2019

スポンサーリンク

シナリオ

Bさんは、レストランを営んでいます。Aさんという従業員を雇っています。

そこに、Xさんという新しいお客さんが来て、5,000円プリペイドカードを作り、4,019円の食事代を支払いました。お客さんは、次回のために、3,000円チャージして帰りました。

処理の流れ

初めからコマンドレベルまで説明すると複雑になるので、先ずは、処理の流れを説明します。
必要に応じて、MIFARE Classic 1K/4Kのマニュアルを参照してください。

プリペイドカードを作る

先ず、Value blockにするブロックを決めます。書き込み可能なブロックは47ブロックありますが、ブロック5に書き込むことにします。

次に、認証キーを考えます。

BさんはAさんに現金を触らせたくありません。
そこで、Aさんが会計業務で使うKey AとBさんが入金業務で使うKey Bを使い分けて運用します。

具体的な値として、Key A“AA AA AA AA AA AA”Key B”BB BB BB BB BB BB”にすることにしました。

ブロック5をValue blockにするため、Access bitsを設定します。Value block用のAccess bitsとしては、(C1, C2, C3) = (1, 1, 0)と(0, 0, 1)の2つ(以下、それぞれ110と001と表します)が用意されています。110はKey A|BでReadとDecrementができます。さらに、Key Bで、WriteやIncrementができます。このプリペイドカードは今後も入金作業を予定しているので、この設定を採用します。001に設定すると、110の設定に対して、Key Bを使ってもWriteとIncrementができなくなります。こちらは、QUOカードのように、使い切りカードとして使うような場合に使います。

次に、Sector trailerAccess bitsを設定します。Key AでKey Bを読み取れないようにするためです。実運用であれば、キーを読むことと設定をすることができなくなる111設定でも問題ありません。しかし、今回は、テスト動作なので、後で元に戻せるように、Access bitをKey Bで変更できる101設定とします。注意点として、101設定では、Key AとKey Bを変更することはできません。Keyを変更したい場合には、一度、Sector trailerのAcess bitを他の値にしてから変更することになります。

最後に、5,000円を書き込みます。

食事代の支払い処理をする

Aさんが担当するレジで、Key Aを使って認証し、4,019をDecrementします。

チャージする

Aさんが担当するレジで、Key Aを使って認証しても、3,000をIncrementすることができないことを確認します。
Bさんが担当する入金機で、Key Bを使って認証し、3,000をIncrementします。

実動作

では実機を使ってテストします。

ここでは、カードとの接続は既にできているものとして、SCardTransmitに投げるコマンドを示します。

カードとの接続方法は、以下に示す記事に書きましたので、必要に応じて参照してください。カードのUIDの取得コマンドの16進数の箇所を以下に示すコマンドに書き換えれば動作すると思います。

Visual BasicからPC/SCを介してNFCカードのUIDを取得してみた
NFCカードのUIDを取得したい 自分の夢に近づくためのステップの一つとして、PCからFeliCa、Mifare等のNFCカードに読み書きする技術の習得があります。 最低限、UIDが取得できれば、あとはアプリケーション側で時間情報や場所...

プリペイドカードの作成

ブロック5用のKey A、Key Bは、対応するSector trailerであるブロック7に書き込みます。
念のため、ブロック7を含むセクタ1の初期状態を確かめておきます。

・初期状態のKey A(FF FF FF FF FF FF)をメモリ0に送信
FF 82 00 00 06 FF FF FF FF FF FF
・ブロック7を指定してメモリ0のKey Aで認証(ブロック4~7のどれで認証しても良い)
FF 88 00 07 60 00
・ブロック4から7のデータを読みます
FF 86 00 00 05 01 00 04 60 00
FF 86 00 00 05 01 00 05 60 00
FF 86 00 00 05 01 00 06 60 00
FF 86 00 00 05 01 00 07 60 00

読み出した結果は以下になりました。

&H04: 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
&H05: 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
&H06: 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
&H07: 00-00-00-00-00-00-FF-07-80-69-FF-FF-FF-FF-FF-FF

ブロック5には全て00が入っています。

ブロック7の最初の6バイトはKey Aです。FF FF FF FF FF FFのはずですが、読み出せないので、全て00になっています。最後の6バイトはKey Bです。FF FF FF FF FF FFが入っていることが分かります。

中央のByte 6~Byte 9は、FF 07 80 69になっています。

これを2進数に直すと、
Byte 6は、1 1 1 1 1 1 1 1
Byte 7は、0 0 0 0 0 1 1 1
byte 8は、1 0 0 0 0 0 0 0
Byte 9は、0 1 1 0 1 0 0 1
になります。

一方、各ビットの意味は、
Byte 6は、C23/ C22/ C21/ C20/ C13/ C12/ C11/ C10/
Byte 7は、C13  C12  C1C10  C33/ C32/ C31/ C30/
Byte 8は、C33  C32  C31  C30  C23  C22  C21  C20
です。ここで”/”は反転を意味します。
Byte 9は好きに使えるUser Dataですが、初期値として0と1を同数だけ入れているようです。

よって、Access bitsは、ブロック4(添え字が0)に対して000、ブロック5(添え字が1)に対して000、ブロック6(添え字が2)に対して000、ブロック7(添え字が3)に対して001であることが判りました。

Sector trailer(ブロック7)のAccess bitsが001なので、Key Aを読み出すことはできないものの、その他の読み書きはKey Aで可能ということになります。

ここで、前述の検討に従って、ブロック5を110に、ブロック7を101に設定します。

すると
Byte 6は、1 1 0 1 0 1 0 1となるのでD5、
Byte 7は、1 0 1 0 0 1 1 1となるのでA7、
Byte 8は、1 0 0 0 0 0 1 0となるので82、
Byte 9は、変更しないで69、
となります。

よって、Sector trailerに書き込むデータは、
AA AA AA AA AA AA D5 A7 82 69 BB BB BB BB BB BB
になります。

では実際に、Sector trailerを書き換えます。

・Sector trailerにKey A, Key B, Access conditionsを書き込む(初期状態のKey Aで認証してから実行のこと)
FF D6 00 07 10 AA AA AA AA AA AA D5 A7 82 69 BB BB BB BB BB BB

引き続き、ブロック5に、5,000円を書き込みます。

先ず、5000を4バイトの16進数に直すと&H 00 00 13 88になります。この値のビットを反転すると、&H FF FF EC 77になります。また、書き込むブロックは&H05であり、ビット反転をとると&HFAになります。

Value blockのデータは、値の非反転、反転、非反転、ブロック番号の非反転、反転、非反転、反転を並べた形式をとります。ただし、リトルエンディアンであることに注意します。すなわち、書き込むデータは、88 13 00 00 77 EC FF FF 88 13 00 00 05 FA 05 FAになります。

・新しいKey Bをメモリ1に送信
FF 82 00 01 06 BB BB BB BB BB BB
・メモリ1のKey Bでブロック7を認証
FF 88 00 07 61 01
・Value blockである05ブロックに5000を書き込む
FF D6 00 05 10 88 13 00 00 77 EC FF FF 88 13 00 00 05 FA 05 FA

ここで、現時点の状況を確認します。コマンドは次の通りです。

FF 86 00 00 05 01 00 04 60 00
FF 86 00 00 05 01 00 05 60 00
FF 86 00 00 05 01 00 06 60 00
FF 86 00 00 05 01 00 07 60 00

読み出した結果を示します。

&H04: 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
&H05: 88-13-00-00-77-EC-FF-FF-88-13-00-00-05-FA-05-FA
&H06: 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
&H07: 00-00-00-00-00-00-D5-A7-82-69-00-00-00-00-00-00

ブロック5に書き込んだデータがそのまま入っているのが確認できます。

また、Sector trailer(ブロック7)のAccess bitsが送信データ通りにD5 A7 82 69が書き込まれていて、Key Bが見えなくなっていることも確認できます。

これで5,000円が入ったカードができました!

食事代の支払い処理をする

食事代の支払い処理として、4,019円をDecrementします。

4019を16進数で表すと、&H 00 00 0F B3です。これをリトルエンディアンで並べてデータとします。

支払い処理は、Key Aでできるはずです。

・メモリ00のKey Aでブロック7を認証
FF 88 00 07 60 00
・ブロック5から4019をデクリメント
FF D8 00 05 04 B3 0F 00 00
・ブロック5を読み出し
FF 86 00 00 05 01 00 05 60 00

Decrement後のブロック5の状態は次の通りでした

&H05: D5-03-00-00-2A-FC-FF-FF-D5-03-00-00-05-FA-05-FA

値はリトルエンディアンで保存されていることを考慮すると&H 00 00 03 D5です。これを10進に直すと、981です。5000-4019=981なので、正常に動作したようです。

3000円チャージする

最後に3,000円チャージします。

3000を16進数で表すと、&H 00 00 0B B8です。これをリトルエンディアンで並べてインクリメントのデータとします。

先ずは、Key Aでトライしてみます。インクリメントはできないはずです。

・メモリ00のKey Aでブロック7を認証
FF 88 00 07 60 00
・ブロック5に3000をインクリメント
FF D4 00 05 04 B8 0B 00 00
→ (SW1, SW2) = (&H69, &H82) Security status not satisfiedのエラー発生

やはり、Key Aではインクリメントできなかったので、Key Bでトライします。

・メモリ1のKey Bでブロック7を認証
FF 88 00 07 61 01
・ブロック5に3000をインクリメント
FF D4 00 05 04 B8 0B 00 00

最終結果は次のようになりました。

&H04: 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
&H05: 8D-0F-00-00-72-F0-FF-FF-8D-0F-00-00-05-FA-05-FA
&H06: 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
&H07: 00-00-00-00-00-00-D5-A7-82-69-00-00-00-00-00-00

最終的な残高は&H 00 00 0F 8Dです。これを10進数に直すと3981であり、981円の残金に対して3,000円がチャージされました。

まとめ

MIFARE Classic 1K/4KのValue blockの動作を一通り確認することができました。Access bitsの設定方法が難解ですが、一度プログラムを組んでしまえば面倒ではないと思います。

残高より大きい値をDecrementすると、エラーが出るかと思いましたが、普通に引けてしまいました。あらかじめ残高を読み出して、Decrementする値より大きいことを確認してから実行する必要がありそうです。そうなると、Key Aで、Incrementを禁止しても、巨大数値をDecrementすることにより、一周回ってチャージできるのかもしれません。試してないから知らんけど。

参考文献

https://www.nxp.com/docs/en/data-sheet/MF1S70YYX_V1.pdf
https://www.nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf
https://www.hidglobal.com/system/files/doc_eol_expired_files/5321-903_b.5_-_omnikey_contactless_scr_developer_guide.pdf
https://www.acs.com.hk/download-manual/7571/USR-ACM1252U-Y3-1.02.pdf

コメント

タイトルとURLをコピーしました