2011年9月11日

モバイルNFCは電源供給型ですよ

タイトル通りだ。
モバイルのMIFAREにせよFeliCaにせよ、電源が必要だ。
カード型は電源がいらないではないか!と思う人もいるかもしれない。
それはカード型だからであって、モバイルの場合は相手からの電波だけではとても電力が足りない。
機能が限定されているカードとは違い、モバイルの場合にはあれこれやらなくてはならないのだ。
まあ、それが理由かどうかは知らないが、とにかく電源がいる。
仕様といえば仕様だが、電源がなければ動かないよね、というだけのことだ。

 

FeliCaチップが載ったシールを携帯電話に貼り付けて「おサイフケータイ」とかいう人もあるかもしれんが、それは違う。
単に「FeliCaチップが載ったシールを携帯電話に貼り付けている」だけだ。

携帯電話の場合、FeliCaを生かすために、先に携帯電話本体を使えなくすることがある。
これが「電源が切れてもしばらくは使えます」だ。
もちろん、FeliCaロックしている場合は使えないだろうが、ここら辺はメーカー次第かも。

 

モバイルFeliCaチップは、カードエミュレーションなので、電波が来るまで待ち受けている。
消費電力は知らんが、けっこう小さいんじゃなかろうか。
NexusSにもPN544というモバイルNFCチップ(SEはSmartMX?)が載っているが、あれの最初はR/Wモードだけだったので、自分で電波を出すって仕様だったと思う。
自分で電波を出すと、消費電力は大きいので、電池がすぐなくなる。
この辺りは、考え方が大味だよなー、と思った。


モバイルのNFCは流行るのかどうかわからんが、私が調べていることは自分以外は役に立たんのだろうなー、と冷めた目で見ている。
流行ったなら、たぶんAPIが提供されて、意識せずに使えるようになるだろう。
流行らなければ・・・APIなどの提供がなくて使おうと思わないだろう。

ユーザにどのくらい使い勝手が許されるかってところですかな。

MIFARE Classicのコマンドは、FeilCaとは違うシーケンス

とうとうmifareというタグが追加されてしまった。
FeliCaはfelicaで、MIFAREはnfcだと不公平というものだ。


さて、今はMIFARE Classic 1Kの読み書きをしようとしている。
読む方は、会社の社員証を使ってできることはわかった(中身と一致しているかどうかは知らんが)。

どうやって当時コマンドを調べたかというと、ACR122というR/W仕様がネットでダウンロードできたので、それをまねしたのだ。
なお、ACR122はPN532内蔵だ。

 

それではなんなので、自分で調べようとしたお話だ。
FeliCa Liteのときと同じように、カードコマンドがわかれば、それをInDataExchangeを使って投げればいいだろうと思っていた。

http://www.nxp.com/documents/data_sheet/MF1S50YYX.pdf

このドキュメントのp.22からがコマンドになっている。
見てみるとわかると、AuthenticationとWriteは2シーケンスに分かれている。
ACR122のドキュメントでは、これらも1コマンドで動作させているので、FeliCa Liteのようにシーケンスとコマンドが1対1になっていないようなのだ。

うーん、コマンドを見てもわからんということか。


PN533のドキュメントを見ると、InDataExchangeのところにMIFARE cardの説明があった。
渡すのは、コマンド、アドレス、最大16byteのデータ部、となっている。
これを渡すと、R/W側がうまいことシーケンスを作ってくれるのだろう。
Writeの場合は1ブロック分のデータだし、Authenticationの場合は6byteが鍵で、4byteがカードのシリアルナンバー。

 

シリアルナンバー?
UIDではないのか?
ACR122ではUIDになっているので、UIDが入るんだと思う。

思うが、4byte限定というのが気になる。
UIDには、7byteと10byteもあるのだ。
PN533のドキュメントには、そこ以外でSerial Numberを指すものは無かった。
やはりUIDなのだろうか。

 

厳密に言えば、4byteのUIDは、ユニークではない。
MIFARE 1Kのドキュメントでは、NUID(Non-Unique ID)と書いている。
Ultralightのドキュメントでは、unique 7byte serial number(UID)、って書いてある。
うーん・・・。
どうでもよくなってきた。

[java]EnumSetはビットフィールドの代わりにはなりにくいように思った

ビットフィールドを使うときって、何ビット目にはこういうデータ、と決まっているものだと思う。
例えばMifareのAccess Bitsなんかは、bit0がこうで、bit1がああで、と決まっている。

 

EffectiveJavaの項目32に「ビットフィールドの代わりにEnumSetを使用する」というものがあるが、これはビット位置が重要でないく、値を単にビットに割り当てて使っている場合にしか有効ではないと思う。
まあ、Javaでの「ビットフィールド」というものがあるのかもしれんが、私が「ビットフィールド」として使いたいときの代わりにはなりにくいな。

 

ならばどうなったらいいかというと、簡単に数値に変換できることだ。
EnumSetでenumの集合体を作っても、それを数値にするためにはぐるぐる回してやらないといかんと思う。
それくらいなら、最初から整数値を使った方が速いよな、というところ。

 

もちろん、安全性という意味ではいいだろうし、ビット幅など考えなくていいから便利ではある。
なので「集合としてビット値を使っているならばEnumSetにしたまえ」というのがいいんじゃなかろうか。


EffectiveJavaの項目30を見て、代表的なシステムコードをintからenumに置き換えていった。
まあまあうまくいったのだけど、結局は数字として使っていないので、あんまりきれいじゃない。
こういうのを繰り返して、慣れていくしかないのだろうな。

Type1~Type4 TagとMIFARE Classic Tag

NFC-Aは、Type1とType2とType4をサポートするらしい(4はオプションかな)。
なんじゃそりゃー、と思ったが、棲み分けがあるらしい。

http://www.nfc-forum.org/resources/white_papers/NXP_BV_Type_Tags_White_Paper-Apr_09.pdf

ここの13ページに、Type1~4とそれに対応する商品が書いてあった。

 

Type1は、Topaz。
Type2は、Mifare Ultralight。
Type3は、FeliCa。
Type4は、DESFireやSmartMX。

 

ほほう。
では、今回私が買ったMifare 1Kみたいな「Mifare Classic」はどれにあたるのだろうか?

 

これは、19ページに図があった。
何と、Type MIFARE Classic Tag、という分類があるではないか。。。
http://www.nxp.com/#/pip/pip=[pfp=53424]|pp=[t=pfp,i=53424]

これのAN1304というやつが、MIFARE Classic Tagのことを書いているようだ。

 

Mifare 1KとかはType1とか2なのだろうと思って買ったのだが、まさかの落とし穴であった。
Amazonの商品名には、Ultralightにだけ「Type2」って書いてあった。さすがOrangeTagsさん。

まあ、遊ぶ分にはMifare 1Kで十分だろう。

2011年9月10日

eclipseはSSH2でAESの鍵をわかってくれない

Ubuntuとcygwinとeclipseで、githubにアクセスしようとする。
すると、Ubuntuとcygwinはいけるんだけど、eclipseではpushができない。
まあ、コマンドで打てばいいや、とやっていたのだが、気になって調べてみた。
どうも、AESは見てくれないらしい。
解決方法は、DES3に変換する、みたいなものしかないらしい。
ファイルが2つできるのは嫌だが、鍵を別にするのも嫌なので、変換した。
http://stuvel.eu/blog/161/eclipse-doesnt-support-aes-encrypted-ssh-keys

こそっとはまったのは、EGitが(なのかはわからんが)eclipseで設定したSSH2の内容をすぐには見てくれないこと。
一度eclipseを再起動させんと反映できなかった。
まあ、そんなもんだ。

2011年9月9日

Parcelableは不思議だ

PaSoRiを使って、FeliCa LiteにNDEFのデータを書こうと思った。
データの見方はわかったのだけど、実装するのはめんどくさい。
きっと、Android提供部分にあるはずだ、と探すと、あった。
android.nfc.NdefRecordだ。

 

これが、Parcelableをimplementsしている。
android.os.Parcelableだ。
よく見るんだけど、使ったことがない・・・。
これを機会に、ちょっと見てみようと思ったのが発端だ。

 

どうでもいいことだが、NdefRecordのパケット解析部分はJNIを呼んでいて、そこからlibnfcを呼んでいた。
まあ、そんなもんだ。


Parcelableで検索すると、いっぱい出てくるので、ここに書くことはなかろう。

ソースを見ると、Parcelable自体はシンプルだ。
特に中身があるわけでもない。

 

作業をしているのは、Parcelの方みたいなので、ちょっと見てみよう。
データを書き込むとき、例えばint型であれば、writeInt()というメソッドを呼び出すようだ。
では、writeInt()のソースは・・・JNIか。
JNIのソースファイルは、android_util_Binder.cpp。
そう、Binder関係なのだ。

Binderは、Android用にkernelに手を加えてまで追加したプロセス間も通信できる機能。
らしい。

 

writeInt(jint)は、binderのParcel::writeInt32(int32_t)を呼び出すくらいのことしかしていない。
binderのParcelは、frameworks/base/binderにある。
ここでもwriteAligned(int32_t)しか呼んでない。
writeAligned()は、テンプレートだ。
sizeof(T)が容量内にあることを確認できたら、reinterpret_cast<T*>した左辺値に代入して、sizeof(T)を返している。

いやあ、びっくりだ。
キャストが左辺に置けるんだ。
これって、gcc拡張かなにかか?
あるいは新しいC++ではそんなことが許可されているのか?
最近どっかでそういう記事を見たような気がする。

とまあ、ここまでで見るのはやめだ。
メモリ効率よりも速度効率を優先してるって感じなのがわかったので、よしとしよう。

2011年9月7日

[無駄]Mifareカードを注文する

朝起きて、パソコンを付け、なぜかMifareカードを注文していた。
しかも、1Kを10枚と、ULを1枚。
なんでだ。。。

まあ、いいや。



Mifareのホームページに、UIDは7byteにしますよー、というようなことが書かれていたので、てっきり7byte品しかないと思っていたが、さっき確認すると1Kは4byteだった。
まあ、ULは7byteだからいいか。

何をしたいというわけでもないのだが、FeliCa Liteのことはだいたいわかったと思うので、違うやつもやってみよう、というところだ。
読み込みはできるのだけど、社員証なので書き込みはさすがにやってないのだ。
あとはNFC-Bだけなんだが、これは買えなさそうな気がする。

2011年9月6日

[felica]片側認証でできること

やってみて、わかることがある。
わかっていたはずなのに、つくって、動かしてみて、「ああ、そういえばそうやん」というようなことだ。
片側認証を実装してみて、久しぶりにそういう感触を味わった。


片側認証を文字にすると、だいたいこんなことになる。

  1. カード発行時、発行者がカード鍵をFeliCa Liteに書き込んで、変更できないようにする。
  2. 使うとき、ユーザは発行されたFeliCa LiteをR/Wにかざす。
  3. R/WはFeliCa Liteを捕捉し、RCブロックにランダム値を書き込む。
  4. FeliCa LiteはRCブロックに書き込まれると、内部でセッション鍵を生成する。
  5. R/WはFeliCa LiteのIDブロック、MACブロックをまとめて読み込む。
  6. FeliCa LiteはMACが読み出される前のブロック情報を使ってMACブロックの値を生成するので、5ではIDブロックを使ったMAC値が取得できることになる。
  7. R/WはRCブロックに書き込んだ値と読み取ったIDブロック値から自分でMAC値を計算する。
  8. R/Wは両者のMAC値を比較し、一致したら自分が発行したカードということがわかる。

大切なのは8番。
一致したとわかるのはR/W、すなわち発行者側だけであって、FeliCa Liteは認証などしていないのだ。
これが片側認証の片側たるゆえんである。

 

悪意のある人が、FeliCa Liteを書き換えようとする際はどうしたらよいか?
どうもしない。
なぜなら、FeliCa Liteへの書き込みは認証を必要としないからだ。
Write Without Encryption。
もちろん、読み込む方も同じである。

コマンド名を見れば、そんなことは初めからわかっていたはずなんだけどね-。
片側認証できたので、なにか書き換えられたくない情報を書くようなソフトを考えていて、ようやく思い至ったのだ。


中身を書き換えられることを防ぐことはできないが、書き換えられたことを判断することはできる。
しくみは、MACと同じようにやることだ。
カードのどこかに、ブロック値をカード鍵で暗号化したデータを書き込めば、次に読み込んだときにMACと同じように答え合わせすることで、誰かに書き換えられたかどうかを判断できる。

まあ、防御策にしかならないのだが、そういった用途ができるのであれば十分に有用である。

2011年9月4日

[java]DESede-CBC暗号化

いろいろとネットの情報を参考にしながら、FeliCa LiteのMAC生成アルゴリズムができた。
そのとき、3DES暗号についてなんとなくわかったので、記録を残しておく。


3DESといってもいくつか細かくあるらしく、FeliCa LiteではCBCという方式を使っている。
これは、前ブロックの3DES計算結果を、次のブロックとxorする方式みたいだ。
それは、IVとか初期ベクタとか呼んでいる。

細かい実装は知らないが、1段目でDESエンコード、2段目でそれをDESデコード、3段目でそれをDESエンコードする。
1, 2, 3段目はそれぞれ別の鍵を用いるのが望ましいらしい(FeliCa Liteでは、1と3が同じ)。
鍵が3つとも同じだと、エンコード→デコード→エンコードとなり、DESエンコード1回分と同じになるという。


DESを3回やって、IVを自分でxorしてみたが、ちゃんと同じ結果になった。
xorする実装は好きこのんでやったわけではなく、FeliCa Liteの説明がそうなっていたからだ。
「IVはxorしてつっこむ」という方式であることを知っていたら、やってないですわ。
この辺りが、知識がないなー、と思ったところですな。

あと、24byteの鍵でちょっと迷った。
FeliCa Liteでは2つの鍵しかないので、24byteをどう埋めるのかというところの記述を探したのだが、見つからんかったのだ。
[0-7]に鍵1、[8-15]に鍵2、[16-23]に鍵1をいれると、とりあえずFeliCa Liteと同じ結果になった。
鍵を3つ別にする場合、[0-7]なのか[16-23]なのか気になるが、まあ今のところわからんですな。


できたのは、こんなソース。
8byte固定なのです。

   1: /**
   2:  * Triple-DES暗号化
   3:  * 
   4:  * @param outBuf        暗号化出力バッファ(8byte以上)
   5:  * @param outOffset        暗号化出力バッファへの書き込み開始位置(ここから8byte書く)
   6:  * @param key            秘密鍵(24byte [0-7]KEY1, [8-15]KEY2, [16-23]KEY1)
   7:  * @param inBuf            平文バッファ(8byte以上)
   8:  * @param inOffset        平文バッファの読み込み開始位置(ここから8byte読む)
   9:  * @param ips            初期ベクタ(8byte)
  10:  * @return        true    暗号化成功
  11:  */
  12: private static int enc83(byte[] outBuf, int outOffset, final byte[] key, final byte[] inBuf, int inOffset, IvParameterSpec ips) {
  13:     int sz = 0;
  14:     try {
  15:         // 秘密鍵を準備
  16:         SecretKeyFactory kf = SecretKeyFactory.getInstance("DESede");
  17:         DESedeKeySpec dk = new DESedeKeySpec(key);
  18:         SecretKey sk = kf.generateSecret(dk);
  19:         dk = null;
  20:         kf = null;
  21:  
  22:         // 暗号
  23:         Cipher c = Cipher.getInstance("DESede/CBC/NoPadding");
  24:         c.init(Cipher.ENCRYPT_MODE, sk, ips);
  25:         sz = c.doFinal(inBuf, inOffset, 8, outBuf, outOffset);
  26:         
  27:     } catch (Exception e) {
  28:         Log.e(TAG, "enc83 exception");
  29:     }
  30:     return sz;
  31: }


dkとkfをnullにしているのは、参考にしたソースがそうなっていたから。
セキュリティのために鍵は使ったら0で埋めてから解放する、なんてことまでやっていた。
うーん、このあたりはJavaの知識がないといかんですな。

メモリリーク(Effective Javaで「意図しないオブジェクト保持」と書いてあるやつ)とかも全然気にしていないので、ここ以外の場所が心配である。

[felica]個別化カード鍵のアルゴリズムはなんでもいい

FeliCa Liteのドキュメントに、個別化カード鍵の標準生成アルゴリズムに関するものがある。
ほうほう、と実装して動かしてみてわかった。

これはあくまで「標準」なので、なんでもいいのだ。

 

そもそもなんでこういう資料があるかというと、FeliCa Liteのカード鍵を全発行カードで一緒にしてしまうと、1枚で総当たりしてカード鍵がわかってしまうと、それを使ってカードを自作するとカード運用者は偽造したものとの区別がつかなくなってしまう。

なので「カードごとにカード鍵を変えてください」とアナウンスしている。
それはわかるのだが、カードごとに鍵を変えるというのはなかなか管理が大変である。
IDmは発行カードごとに違ったとしても、運用側がそのカード番号をすべて管理するのは面倒。
そういった番号は管理せず、カードだけでなんとかしてしまえるところに利点があるのだと思う。

 

そこで、個別化マスター鍵というものを用意し、それとカードごとにIDブロック情報からカード鍵を作成しましょう、というのが個別化カード鍵である。
管理者側は、個別化マスター鍵だけ管理しておけばいい(鍵バージョンもあるけど)。
IDブロック情報はカードごとに異なるので、個別化マスター鍵から作られる個別化カード鍵はほぼ一意に決めることができる。
その決めるアルゴリズムが提供されているのである。

 

なので、別にこのアルゴリズムでなくても、カードごとに持っている情報で一意に定まる個別カード鍵が生成できさえすれば何でもいいということになる。

そういうことが、実装してみて、動作確認をしようと考えたときにわかった。
IDが異なれば個別カード鍵も異なることはわかったし、同じIDに対しては同じ個別カード鍵を生成することはわかったけど、それ以上の確認方法が思いつかないのだな。。。