緒言
パソリってのは,SONYのUSB接続非接触ICカードリーダー.
電子マネー付きカードとか,おサイフケータイとか,交通系ICカードとか,ああいう非接触のICカードの情報を読むことができます.

さて,これで何をするかというと,事前に登録しといたカードを読み込ませたときに認証okとする.という単純なこと.

今回の認証には,Felicaにユニークに割り当てられているらしい「card IDm」という値を利用します.

一応,card IDmはユニークだけれど,偽装可能なので高いセキュリティレベルを求められる認証に使うべきではないらしいです. あくまでも簡易認証ということで.

環境
Ubuntu Server 12.04LTS
PaSoRi RC-S330
libusb 0.1.12-20
libpafe_0.0.8-1_amd64
PostgreSQL 9.1
PHP5.3
PEAR::System_Daemon 1.0.0

ライブラリインストール
まず,Ubuntu12.04でPasoriを使うためのライブラリをインストールします.
libusbはaptで入れます.

$ sudo apt-get install libusb-dev

libpafeは公式サイトからdebパッケージをダウンロードしてインストール.

$ wget http://homepage3.nifty.com/slokar/pasori/libpafe_0.0.8-1_amd64.deb
$ sudo dpkg -i libpafe_0.0.8-1_amd64.deb

確認
libpafeをインストールすると,pasori_testとfelica_dumpというコマンドがインストールされます.

$ pasori_test
PaSoRi (RC-S330)
firmware version 1.30
と,pasori_testは接続されているPaSoRiの情報を表示してくれます.
接続されていないときは,
error
とだけ表示されました.

$ felica_dump
# lpdump : Wed Mar 12 22:55:03 2014
# --- IDm info (FeliCa) ---
# Manufacture Date = 2013/1/17
# SN = 22462
# Manufacture Code = 2901
# Equip. Code = 0200
system num 3
# FELICA SYSTEM_CODE = FE00
# card IDm = 0129************
# card PMm = 0113************
# area num = 7
# service num = 38
# AREA #0 = 0040 (00000)



PaSoRiにfelicaカードを載せてfelica_dumpを実行すると,こんな風にカードの情報がツラツラ~っと表示されます.
カードによって表示される行数は様々で,短いのは30行ちょっと,長いやつは150行ぐらいだったり.
細かい内容は知りませんし知ろうともしてません.今回はこの9行目に出てきてる"card IDm"というやつしか使いませんので.

仕様
今回作るスクリプトは,以下のような動作をします.

・無限ループ動作でfelica_dumpをしまくる.
・felicaカードが載せられると,card IDmを読み取る
・card IDmを事前にDBに登録しているものと照らし合わせて,
 それぞれの反応をする.

DBへの登録スクリプトは作っていません.直接SQLを打って登録しました.

DBテーブル定義は以下の通り.

# \d pasori
  Column  |          Type          | Modifiers
----------+------------------------+-----------
 idm      | character(16)          | not null
 username | character varying(48)  |
 status   | integer                |
 comment  | character varying(256) |

idmにcard IDmが入ります.usernameとcommentは,誰の何のカードか分かりやすいように入れれるようにしてます.
後々何かに使えるかと.
statusには,認証okのときは1を入れておき,一度登録したカードを消したいときに,その行を削除するのではなく,statusを0にしておくことで認証できないようにするという風にしました.

# SELECT * from pasori ;
       idm        | username | status |   comment
------------------+----------+--------+--------------
 012************* | genki    |      1 | genki-203sh
 010************* | genki    |      1 | genki-icoca
 011************* | genki    |      1 | genki-pitapa
 011************* | genki    |      0 | genki-plusex
一応,テスト用にこんな感じで手元にあるカード類を登録しておきました.
203shってのは,おサイフケータイ.

実装
PHPで書いています.
無限ループをデーモンとして動作させるため,PEAR::System_Daemonを利用しました.

>>>ソース全体はこちら<<<

以下,ところどころ説明.

$str = `felica_dump | grep "card IDm"`; // カード情報からIDmを取得する.
//カードが無ければ$strは空になる.
if($str){
    $str = str_replace(" ",null,$str);
    $str = preg_replace("/\n.*/",null,$str);
    list(,$str) = explode("=",$str); //ここで$strの中身はcard IDmだけになってる.
felica_dumpのうち,"card IDm"のある行だけを抜き出してるんですが,card IDmの行は,
# card IDm = 0129************
ってなってるので,スペースを消して"="で区切った2番目を抜き出します.
ただ,カードによってはcard IDmが複数あるときがあるので,"="でexplodeする前にpreg_replaceで改行以降を削除してます.

usleep(100000); //ちょっと待ってループ
System_Daemon::iterate(0);
あんまりどんどんループするのもどうかと思って,0.1秒だけスリープしてます. ただ,felic_dump自体,そこそこ時間がかかる処理なので無くても良いかも. System_Daemon::iterate(0);に関しては,引数に数字を入れたらその秒数待ってくれるけど,1秒単位なのでそれは待ちすぎかと思ってusleepを使いました. ただ,System_Daemon::iterate()自体は,キャッシュを掃除してくれるという情報があったので,分からないけど入れときました.

テスト

上で公開したソースとはちょっと違う古いバージョンですが,だいたいこういう動作です.
上記のテーブルの通り,ケータイ・ICOCA・PiTaPaは認証ok.
Plus EXカードは登録してるけどstatusが0になってるのでエラーが出ます.
クレジットカードはiD付きなのでfelicaカードですが,DBに登録してないのでエラーが出ます.

応用
これができたからどうだっての?って話ですが,RBIOを利用して,ホームオートメーションの一部として使おうと思ってます.
具体的には,玄関の鍵とガレージのシャッターを,これで開けられるようにしようかと.
それはまた別にまとめて記事にします.

謝辞
libpafe
簡単に使えるツールを公開していただき,とても助かりました.

PHPのプログラムをデーモンとして動かしてくれるPEAR::System_Daemon(A Day In The Boy's Life)
PEAR::System_Daemonの利用に際して,大変参考にさせていただきました.