カテゴリー
Tech Utility

FeliCa / PaSoRi 研究 | AmpiTa Project

 今年に入ってGateKeeperの見直しを行いました。

 改めて非接触ICカード『FeliCa』をどのように活用するか検討したので、備忘録的にblogに残しておこうと思います。




FeliCaとは?

 『FeliCa』は『フェリカ』と読みます。

 SONY(ソニー)が公式にサイトを立ち上げていますので詳細はそちらをご覧いただくと良いのですが、トップページのバナーではSuicaを用いた自動改札、マイナンバーカードを用いた保険証などを紹介しています。

 特徴は『非接触』であることと『非光学』であることだと言えます。




接触型カード(非光学)

 典型例としてはクレジットカードです。磁気部分にカード情報が入っており、リーダーを物理的に通す際に磁気部分を走査する仕組みです。


 難点としてはカードリーダーが必要であること、そのリーダーは安くはないこと、走査する際に一定程度の時間がかかることなどが挙げられ、使用できる場所が限られていました。

 また『スキマー』という装置でカード情報を不正に入手するスキミング被害が多数報告されています。


 磁気を使っているため、磁石などに接するとカードが使えなくなるのも弱点です。
 筆者も先月、三井住友銀行の窓口を訪ねてカード交換を依頼したところです。

 会計など多少の時間は許される場面では有用ですが、受付に使うには多少混雑の覚悟が必要です。




光学型カード(非接触)

 典型例としてはポイントカードなどに使われているバーコードです。
 文字や数字が黒と白の2色のバーで表現されているもので、頑張れば人間の目で見てバーコードを復号して文字情報に変換できます。

 利点としてはバーコードリーダーが比較的安いこと、リーダーとバーコードを概ね垂直の位置にするだけで走査できる曖昧さから素早く走査できることなどが挙げられると思います。
 また、バーコード自体は印刷で作れるため、個人別のコードをラベルライタ(テプラなど)で作成したり、外装に初めから印刷しておくような運用がタダみたいな低費用で出来る点が普及の理由になっていると考えられます。

 物流ではバーコードは欠かせないアイテムであり、コンビニ等ではバーコードリーダーが普遍的に設置されているため、Tポイントカードなどもバーコードを利用して普及の障壁を取り除いたのではないかと考えます。

 欠点としては偽造が容易である事、印刷なので擦れて消えてしまうことがあること、バーコードが在る程度は平らになっていないと走査できないため袋状の物や極端に小さい物などには適用しづらい点が挙げられます。

 バーコードの欠点を補う技術として二次元コード(QRコード)が普及しています。




非接触・非光学

 接触型の磁気カードと、光学型のバーコードの課題を解消するカードとして非接触・非光学のICカードがあります。

 カード内のICに非接触でアクセスする方式です。

 一瞬でデータをやり取り、ICチップは偽造困難、磁石などを近づけても簡単には損壊しない堅牢性、リーダーは数千円の廉価品でも十分機能するということで広く使われています。

 高度な技術を搭載したものではマイナンバーカードや運転免許証にも使われています。




研修受付の自動認識

 今回は研修会の受付時の入退管理に自動認識を導入しようと考えての非接触ICカードの活用検討です。

 15年ほど前にRFIDを使った研究で自動認識は導入済ですが、当時はFeliCa(NFC)ではない産業用のRFIDでした。いまでも大量にRFIDタグが残っていますが、リーダーが普及機種ではないのであまり使い物になりません。

 10年ほど前にはNFCにもチャレンジ、既にGateKeeperのversion 1としてVectorでもリリースしていましたが、今回はリニューアルを機に見直しもしてみました。




結論から言えばFelicaLib

 GateKeeper の version 1 の開発言語は Visual Basic でしたが、今回の version 2 は C# なのでプログラム自体はゼロから作り変えです。

 FeliCa との通信も改めてプログラムを作り直すことになりましたが DLL(Dynamic Linking Library)は前作と同じ『felicalib.dll』を使うことになりました。
 DLLとは、プログラムを支援するプログラム、部品やアクセサリとでも言った方が良いのかもしれませんが、DLL自体が単体で動作することはありませんが、私たちがつくるプログラム(EXEファイル)の一部の機能を、私たちが開発することなく補完してくれます。

 SONYのPaSoRiというFeliCaリーダーの普及機種にはいくつもの DLL が存在しますが、今回使用した DLL は felicalib.dll というものでした。




なぜfelicalib.dllに?

 『felicalib.dll』のプロパティを見るとバージョンは『0.4.1.1』、著作権は『2007 – 2008 Takuya MUrakami』となっています。
 すなわち、felicalib は version 0.4.1.1 が現行品であり、その著作権者はムラカミ・タクヤさんで、制作時期は2008年頃であることがわかります。

felicalib

[Download] GitHub: tmurakam, felicalib ver 0.4.2


 『Takuya MUrakami』で検索すると以下の2つのサイトに『felicalib』の記載があるので、おそらくこの方が『Takuya MUrakami』さんなのだと思います。

Takuya Murakami(GitHub)
Takuya Murakami’s website

 さて、なぜ15年も前のDLLを採用したかと言うと、処理の速さで圧倒的であったためです。

 これは筆者のプログラミング能力の問題だと思いますが、いくつか試した DLL は、いずれもPaSoRiとの通信確立とカード走査が一体となっているようなプログラムを組む必要がありそうでしたが、felicalib.dllは分離できました。

 この分離によって、リーダーとの通信確立にかかる負荷は最初の1回で済むので、2回目以降の走査に負荷がかからなくなります。

 単回の走査で終わる使い方だどれも差が無いというか、むしろfelicalib.dllは手間が多かったです。
 GateKeeperでは自動改札のように使いたかったので、1秒間に数回の走査を実施したく、しかも単回走査ではなく次々と来る人々を何度も走査したいので、処理が重いと破綻してしまいます。

 GateKeeperの標準設定では250msecの走査頻度、すなわち1秒に4回のスキャンを実施していますが、何ら問題ありません。
 カードをたくさん並べて、リーダーをサッと動かしても、すべてのカードを走査できます。
 残念ながら、他のDLLを使うと1枚1秒以上の時間を要し、ときどき走査できないカードもあって、もたついてしまうことがわかりました。




felicalib.dllでクラッシュの要因はマイナカード

 『felicalib.dll』が完璧かと言うと、そうではありません。

 いまのところ謎が解けていないので非対応としているのがマイナンバーカードや運転免許証、産業用のNFCなどです。
 ざっと『FeliCa以外』というくくりで良いと思いますが、カードタイプの『FeliCa』に分類されるもの以外を走査するとクラッシュしてしまいました。

 要因はわからないのですが、他のプログラムを使うとPaSoRiでFeliCa以外のマイナンバーカードや運転免許証は走査可能なため、ハードは対応しているがソフトが対応していないということだと思います。

 筆者が保有するあるカードのUIDは『01-14-B4-CD-F1-15-2E-0D』でした。このカードは何にも使われていないのでUIDの悪用の方法もないと思いますので見られても良いのですが、ハイフンで区切られた8ブロックの16進数で構成されています。

01-14-B4-CD-F1-15-2E-0D

 これが運転免許証になると、単純に走査しただけでは下記のように読み取れました。

00-00-00-01

 ブロック数は半分の4つです。
 教科書どおりに言うと2進数の0000~1111までの16個の数字を16進数であれば1桁で表現できます。16進数2桁のブロックは256通りの表現に使う事ができます。256通りと言えば1byteです。
 FeliCaは8byte分のUIDを返してくるのに対し、運転免許証が4byte分しか返して来ないのでエラーというか、想定外処理になっているのではないかと推測します。

 IEC14443のType-Aのタグを走査した際も同様にクラッシュしました。

46-53-54-4E-31-30-6D

 上記の通り7byte分しかUIDがないので処理できなかったと推測します。
 何らかの方法で補数を与えられればこの処理はできると思います。所感としては『FelicaLib.cs』にある『byte[] IDm』の中の『new byte[8]』の『8』を7にしてしまうか、最初から不足byte分に『00』などを入れておいてレスポンスを待つのが良いのかなと思っていますが、実装方法がわからないので放置しています。




FeliCa以外の検出方法

 いまのところハッキリした方法はわかっていませんが、姑息的な方法によりクラッシュは免れるようになりました。

 下記のコードを felicalib.cs 内に設置しています。要点は『 == -1』の部分です。

 PaSoRiから返ってくるポートの『pasori_init』は、初期設定どおりで何も起こっていなければ『IntPtr.Zero』なので最初の if でエラーを返します。

 何らかの設定変更が起こった、すなわちPaSoRiとの通信は実行されたとなると『IntPtr.Zero』ではないので次のifにいく訳ですが、正常値は『0』なので、DLL作者がサンプルとして公開してくださっているCSファイルには『!= 0』としてゼロ以外ならエラー処理する指示が記されています。
 ここを書き換えました。
 FeliCa以外のNFC規格のカードをPaSoRiが走査するとカードは認識するがfelicalibでは処理できないので『-1』を返します。したがって『-1』を検出したら『-1』としてエラーを受け入れますという記述を加えました。

 これがなぜ良いのかわかりませんが、クラッシュは回避できるようになりました。
 ただし、この『-1』を検出するのは瞬時であるとしても、処理には1~2秒かかってしまうので瞬時性はありません。

 この『-1』の検出用にint変数を使っていますが、どうやら『pasori_init(pasorip)』は1回目の読込で正規の値を渡したあとは一律に『0』を返すようなので、最初にint変数に入れてから処理しないと想定どおりには動かない可能性があります。
 この『int_PaSoRi_Port = pasori_init(pasorip)』処理が非常に時間がかかる処理になっています。FeliCa走査の場合は0.1秒という感覚、FeliCa以外のNFCは10倍以上という体感です。

public Felica()
{
    pasorip = pasori_open(null);
    int int_PaSoRi_Port = pasori_init(pasorip);
    if (pasorip == IntPtr.Zero)
    { throw new Exception("走査処理に必要なDLLを開けません。"); }
    if (int_PaSoRi_Port  == -1)
    { throw new Exception("走査に必要なポートが開きませんでした。");}
    else if (int_PaSoRi_Port  != 0)
    { throw new Exception("走査に必要なポートが開きませんでした。"); }
}

 pasprip には『205533024』などの数字が入ります。これはポートの何らかの数字だと思いますが、解読できていません。

 8回実施して、8回とも違う数字が入りました。

  1. 205984256
  2. 205984352
  3. 205984304
  4. 205984400
  5. 205984448
  6. 205980848
  7. 206004520
  8. 206003992

 似ている『felicap』には『0』が入ります。
 正常ですと『206005912』『206007632』などが入りますが、この数字はpaspripとは異なるものが入ります。

【参考】GitHub: tmurakam, felicalib, FelicaLib.cs




羊を上手く使えれば

 今回初めて利用させて頂いた『SheepSmartCard』というDLLは、非常に簡単にプログラムに搭載できたので、今後の活用が期待できるなと思いました。
 『sheep』なので羊です。

 残念ながら、筆者の技術では高速化できなかったのでGateKeeperには採用していません。

 以下のコードは失敗事例です。Timerに走査コードを入れて、250ミリ秒や500ミリ秒の頻度で読み取っていても、カードが読まれるのは1~2秒後であったり、それでも読めなかったりします。
 おそらく、ハードウェアとの通信に問題があると思います。ハードに合わせたソフト設計が必要なことを実感するコードです。

[DllImport("SheepSmartCard64.dll", EntryPoint = "GetSmartCardUID")]
extern static int GetSmartCardUID64(byte[] SCardIDm);
[DllImport("SheepSmartCard32.dll", EntryPoint = "GetSmartCardUID")]
extern static int GetSmartCardUID32(byte[] SCardIDm);
delegate int CallDllDelegate(byte[] SCardIDm);

private void timer_sheep_Tick(object sender, EventArgs e)
{
    CallDllDelegate GetSmartCardIDm;
    if (Environment.Is64BitProcess)
    {GetSmartCardIDm = GetSmartCardUID64;}
    else{GetSmartCardIDm = GetSmartCardUID32;}
    var bRecvBuffer = new byte[128];
    var ret = GetSmartCardIDm(bRecvBuffer);
    var idm = Encoding.ASCII.GetString(bRecvBuffer).TrimEnd('\0');
    switch (ret)
    {
        case 0:
            this.txb_NFC_Log.Text = idm;
            break;
        case 100:
            this.txb_ErrMsg.Text = "カード読取不可";
            break;
        case 200:
            this.txb_ErrMsg.Text = "カードリーダー認識不可";
            break;
        case 300:
            this.txb_ErrMsg.Text = "DLL不在";
            break;
        case 400:
            this.txb_ErrMsg.Text = "UID取得失敗";
            break;
        default:
            this.txb_ErrMsg.Text = "予期せぬエラー";
            break;
    }
}
public  string str_DLL_SheepSmart = System.AppDomain.CurrentDomain.BaseDirectory + "\\" + "SheepSmartCard64.dll";

【参考】Sheep Smart Card Dll




PC/SCも上手く使えず。

 GitHubでもよく見かけるPCSCを使ったコードを応用してみましたが、やはりここでもハードウェアの検出に手間取ってしまい、自動改札機みたいな連続した入場に見合ったプログラムは作れませんでした。

【参考】GitHub: danm-de, pcsc-sharp, ISO7816-4, Program.cs

【参考】GitHub: danm-de, pcsc-sharp, ConnectedReaderStatus, Program.cs




ハード確認を省ければ

 PaSoRiの初期設定のようなコードを外して、何度でも簡単にアクセスできるようになれば felicalib.dll 以外の DLL も利用できると思います。

 『SheepSmartCard』も『PCSC』もIEC14443のType-Aに対応しているようなので、タグの種類が増えてもGateKeeperを対応させられそうです。

 ただし、FeliCaは何らかの形で保有している人が多いので、ある程度の限られた範囲での入退管理であればFeliCa専用でも良いのかなと思いました。




タグの知識

 まだまだタグ型の商品知識が無いので、これから勉強します。

 例えば、ランドセルなど小学生のアリバイ管理に使われるようなタグは何なのか、わかっていません。
 通塾や中高生の通学では交通系ICカードを使った自動認識が行われていますが、これは通学定期や公共交通機関乗車の機会があるから出来ることだと思います。

 GateKeeperは受付管理に志向していましたが、物品の貸借にも応用できるので、物品に付属するようなタグについても研究が必要です。
 無人コンビニが現実化した現在、短期消費のモノにもタグが付く可能性があり、当然ながら長期的に使用する電気製品や書籍などにもタグは付くと思います。

 GateKeeperの進化のため、NFCやFeliCaに関する研究の必要性を実感しましたので、実行に移したいと思います。