2012/01/14

[mifare]Mifare Ultralightに書き込めるか?

Qtをやる、といっておきながら、ちょっと気になることが残っていた。
SDK for NFC starter Kitのサンプルに、Mifare Ultralightへの読み込みサンプルはついていた。
では、書き込みはどうやるのだろうか?
週末なので、調べておこう。
すべての解釈は「私が知っている範囲で」という一文が省略されているものと考えておくれ。

Mifareは種類がたくさんある。
Wikipedia(日本語)では、以下となっている。これは規格的な見方になるか。
  • Mifare Standard
  • Mifare T=CL
  • Mifare DESFire
  • Mifare DESFire8
  • Mifare Plus
本家Wikipedia(英語)では、こうなっている。これは製品ということになるか。
記載が充実している。
  • Mifare Classic
  • Mifare Ultralight
  • Mifare Ultralight C
  • Mifare DESFire
  • Mifare DESFire EV1
  • Mifare Plus
  • Mifare SAM AV2
mifare.netの歴史では、以下となっている。
  • Mifare Classic 1k
  • Mifare Pro
  • Mifare ProX
  • Mifare Ultralight
  • SmartMX
  • Mifare Classic 4k
  • Mifare DESFire
  • Mifare DESFireSAM
  • Mifare DESFire EV1
  • Mifare Ultralight C
  • Mifare Plus
  • Mifare SAM AV2
ざっと見通したい場合は、英語版Wikipediaの項目がよくまとまっていると思った。



私の手元にあるのは、Mifare Classic 1kとMifare Ultralight。
この2種類は、ISO14443規格とは微妙に外れているらしい。
外れているというか、暗号化に関してはNXP独自のもの、と書かれている。
Ultralightは、Classicのセキュリティ部分(と、ちょっと異なるコマンド)が除外されている、とある。
なので、UltralightはほぼISO14443規格に沿っているということであろう。
SDK for NFC Starter KitがUltralightだけになっているのも、そのあたりの事情であろう。
これをFeliCaにあててみると、Mifare Classic = FeliCa Standard、Mifare Ultralight = FeliCa Lite、という構図になろうか。



では、R/Wの立場からClassicとUltralightを見てみよう。
カードの見分け方は、SENS_RES(ATQA)とSEL_RES(SAK)で行う。
取得は、InListPassiveTargetコマンドを使う。送信するのは、SENS_REQ。
そうするとSENS_RESに、SENS_RES、SEL_RES、NFCID1(UID)が戻ってくる。
カードの種類だけであれば、SEL_RESを見るとよい。
どうやって見分けるかという情報は、NFC Forumドキュメントにはなく、NXPの以下を読む必要があろう。
http://www.nxp.com/documents/application_note/130830.pdf
これのp.7に、SAKの各ビットがもつ意味をフローチャートで記載してある。
なお、LSBは「bit1」になっているので要注意だ。ISO14443のドキュメントがそうなっているらしい。
全ビットに意味があるのであれば値一致で判別できそうなのだが、意味がよくわからないのでフローチャート通りに確認していくしかなさそうだ。
とりあえず、表にしてみた。「-」のところはN/Aだ。
b7 b6 b5 b4 b3 b2 b1 b0 Result
- - - - - - 1 - RFU
- - - 1 1 - 0 - Mifare 4K
- - - 0 1 - 0 0 Mifare 1K
- - - 0 1 - 0 1 Mifare Mini
- - 0 0 0 - 0 - Mifare Ultralight
- - 1 0 0 - 0 - ISO14443-4(RATS+PSS)
- - - 1 0 - 0 0 Mifare Plus 2K SL2
- - - - 0 - 0 1 Mifare Plus 4K SL2
ISO14443-3対応かどうかだけみたいのであれば、bit5だけ見ればよい(p.6参照)。
bit5、というのは、ドキュメントで言うところの「bit6」ね。
SENS_REQというのはNFC Forumでの名前なので、ISO14443では別の名前なのかも(REQA ?)。
SENS_RESやSEL_RESはNFC Forumでの名前で、ATQAやSAKはNXPドキュメントに書かれている名称だ。

では、ようやく本題のUltralightを見ていこう。
カードの仕様は、以下のドキュメントに記載されている。
http://www.classic.nxp.com/acrobat_download2/other/identification/M028634_MF0ICU1_Functional_Spec_V3.4.pdf
サポートしているコマンドは、以下。
  • REQA(0x26)
  • WUPA(0x52)
  • Anticollision CL1(0x93)
  • Select CL1(0x93)
  • Anticollision CL2(0x95)
  • Select CL2(0x95)
  • Read(0x30)
  • Halt(0x50)
  • Write(0xa2)
  • Compatibility write(0xa0)
NFC ForumのType2 Tagでは、以下のコマンドになっている。
  • READ(0x30)
  • WRITE(0xa2)
  • SECTOR SELECT(0xc2)
0xc2 ?
Ultralightのコマンドにないのであせったが、SECTOR SELECTは1KBずつに分けられたセクタを選択するコマンドのようだ。
Ultralightは64byteなので、コマンドが不要ということだろう。
書き込みコマンドが2種類ある。
0xa2と0xa0。
NFC Forumは0xa2。
0xa0の方は過去との互換用か何からしく、引数に16byte指定できるものの書き込まれるのは最初の4byteだけらしい。
なので、使うコマンドは0xa2でよかろう。
よく見ると、Writeコマンド0xa2も4byteしかデータが渡せない。
メモリマップを見ると、4byte×16ページ=64byteなのだった。
そのうち2ページはシリアル番号(UID)で、もう2ページもLockとかOTPとかで使われている。
実際に使えるページは12ページで、48byte分みたいだな。
(LockとOTPもユーザエリアとして使用できるとあるが、意味を持っているのでね。)
注意点としては、Writeはレスポンスを返さない、ということかな。

というわけで、SDK for NFC Starter Kitでのサンプルを作成。

githubにも置いた。
https://github.com/hirokuma/NfcStarterKitWrap/tree/master/UltralightReadWrite
ラッパがだんだん充実してきたので、作るのが楽になってきた。
タイムアウト時間などを調整したり、アクセスする部分を別スレッドに移動させたりするとアプリ的にはいいんだろうけどね。

6 件のコメント:

  1. すみませんが、SENS_RES(ATQA)とSEL_RES(SAK)の読み取り方を教えてもらえないでしょうか。
    PasoriのRC-S380でMifareのSENS_RES(ATQA)とSEL_RES(SAK)を読み取りたいです。
    PC/SCのコマンドで取得できるかと思っていましたがInListPassiveTargetコマンドでしょうか。

    返信削除
    返信
    1. ATQBだと取得できそうなのですが、ATQAはよくわからないですね。
      https://docs.microsoft.com/ja-jp/windows-hardware/drivers/nfc/pc-sc-interface
      https://github.com/danm-de/pcsc-sharp/blob/master/Examples/CardStatus/Program.cs

      PC/SCの仕様書からそれっぽい値を返すコマンドを探してみるのがよいかと思います。

      削除
    2. https://stackoverflow.com/questions/23404314/determine-card-type-from-atr

      削除
  2. hiro99maさんありがとうございます。
    以前に少しやり取りさせて頂いて相当久しぶりなのでご返信いただけて大変ありがとうございます。

    PC/SCではATRはSCardChangeで取得できるのでカード種別等はATRで判別していましたが、SENS_RES(ATQA)とSEL_RES(SAK)を読めないかと問合せがあり、調べたらhiro99maさんの記事ばかり出てきたので問合せさせてもらいました。
    やはりリーダライタ専用コマンドがあれば取れそうですが、難しそうですね。
    ありがとうございます。

    ちなみにInListPassiveTargetはどのように送るのでしょうか?

    返信削除
    返信
    1. >ちなみにInListPassiveTargetはどのように送るのでしょうか?

      R/WがPC/SC用のコマンドを理解できるわけではないと思うので、アプリ層でPC/SCコマンドを受け取ったら、ミドル層か何かでR/Wのコマンド(InListPassiveTargetなど)でデータを取得し、最後にアプリ層でPC/SC用のレスポンスに置き換えているんじゃないかと思ってます。
      生のInListPassiveTargetなどのやりとりはR/W依存になるので、メーカーによりけりになるんじゃないかと思います。

      削除
  3. なるほど。そういう流れですね。
    丁寧にお教えいただきまして、ありがとうございます。
    ブログを継続されているhiro99maさんを尊敬します。
    いつかお会いできたら嬉しいです。
    ありがとうございます。

    返信削除

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。

注: コメントを投稿できるのは、このブログのメンバーだけです。