Help us understand the problem. What is going on with this article?

NFC・Felica(Suica, PASMO)でSESAMEを解錠出来るようにする

More than 1 year has passed since last update.

注意

この記事は nfcpyPython2 にしか対応していなかった頃に実装した記録です。
現在、 nfcpyPython3 に対応しています。

環境変数について

私は環境変数を使う形で書いたのですが、一般ユーザーで環境変数を設定した後
raspbian では sudo すると環境変数がリセットされてしまうので、
sudo 時に環境変数を引き継ぐようにするか sudo 無しで使えるようにする必要がありました。

sudo 無しでRC-S380を使える様にする

参考: Suicaでセサミを解錠 ( with ラズパイ)
参考: 交通系電子マネーカードを 黒PaSori RC-S380 + Raspberry Pi + python で

> lsusb

> sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"06c3\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'
> sudo udevadm control --reload-rules
> sudo reboot

ATTRS{idVendor}==\"XXXX\", ATTRS{idProduct}==\"YYYY\" の部分は使っているカードリーダーによって固定の値です。
Sony の PaSori RC-S380 なら上記のコピペで平気なはずです。
参考: USBのベンダーIDとプロダクトIDの話
違うリーダーの場合 lsusb の値を参照しましょう。

sudo 時に環境変数を引き継ぐようにする場合

こちらの記事を参考にしました
参考: sudo時に環境変数PATHが引き継がれない時の対策
上記記事では PATH に限定していますが、 PATH 以外の環境変数を引き継ぎたい場合も visudo で同様にできます

root になってから visudo を実施します

$ su -
# visudo

env_resetsecure_path をコメントアウト(先頭に # を足)します
そして、次の一行を追加します

Defaults    env_keep += "<引き継ぎたい環境変数>"

env_keep に加えることで引き継がせることが出来ます。

実装

これらの記事を参考にしました
参考: Suicaでセサミを解錠 ( with ラズパイ)
参考: PaSoRiでNFCタグとFeliCaを読み込んで出力
参考: Raspberry PiにNFCリーダを接続してSuicaを読み取る

環境変数から情報を取る

Python の話です。
Felica における IDm や NFC の UID 、APIのAuthTokenといった秘匿しておきたい情報についてはコード上に載せず、
環境変数から取ってくるようにしました。

os.environ.get() コマンドで取得出来ます

API

必要なAPIコールはそれぞれ関数として定義しておきます

操作

APIについて

def control_sesame(device_id, command):
    url_control = "{0}/sesame/{1}".format(api_endpoint, device_id)
    head_control = {"Authorization": auth_token,
                    "Content-Type": "application/json"}
    payload_control = {"command": command}
    response_control = requests.post(
        url_control, headers=head_control, data=json.dumps(payload_control))
    return response_control

結果確認

APIについて

def check_sesame_task(task_id):
    url_check = "{0}/action-result?task_id={1}".format(api_endpoint, task_id)
    head_check = {"Authorization": auth_token}
    response_check = requests.get(url_check, headers=head_check)
    return response_check

解錠

def unlock_sesame(device_id, card_id):
    response_control = control_sesame(device_id, "unlock")
    if response_control == None and not hasattr(response_control, "_content"):
        return False

    # print vars(response_control)
    response_control_content = json.loads(response_control._content)
    task_id = response_control_content["task_id"]
    print "task_id = {0}".format(task_id)

    time.sleep(7.0)
    response_check = check_sesame_task(task_id)
    if response_check == None and not hasattr(response_check, "_content"):
        return False

    response_check_content = json.loads(response_check._content)
    # print response_check_content
    if response_check_content["status"] == "processing":
        print "processing"
        pass

    if response_check_content["status"] == "terminated":
        result = response_check_content["successful"]
        print "[{0}] card_id: {1} device_id: {2} unlock: {3}".format(datetime.now().strftime("%Y/%m/%d %H:%M:%S"), card_id, device_id, "successful" if result else "failed")
        return

メイン

if __name__ == "__main__":
    # NFC接続リクエストのための準備
    target_req_nfc = nfc.clf.RemoteTarget("106A")
    # Suica接続リクエストのための準備
    target_req_felica = nfc.clf.RemoteTarget("212F")

    print "Waiting for NFC..."
    while True:
        # USBに接続されたNFCリーダに接続してインスタンス化
        clf = nfc.ContactlessFrontend("usb")
        # NFC待ち受け開始
        # clf.sense( [リモートターゲット], [検索回数], [検索の間隔] )
        target_res = clf.sense(target_req_nfc, target_req_felica, iterations=int(
            TIME_cycle//TIME_interval) + 1, interval=TIME_interval)

        if target_res != None:
            print target_res
            print vars(target_res)
            if not hasattr(target_res, "_brty_send"):
                continue

            brty = target_res._brty_send
            if brty == "106A":
                tag = nfc.tag.activate_tt2(clf, target_res)
            elif brty == "212F":
                tag = nfc.tag.activate_tt3(clf, target_res)
            else:
                continue

            print tag
            if tag != None:
                print vars(tag)
                print tag.type

                # Felica
                if hasattr(tag, "type") and tag.type == "Type3Tag":
                    tag.sys = 3
                    idm = binascii.hexlify(tag.idm)
                    print "Felica detected. idm = {0}".format(idm)
                    if idm in key_idms:
                        unlock_sesame(device_id, idm)

                # NFC
                else:
                    if not hasattr(tag, "_nfcid"):
                        print "Error: tag doesn't have nfcid"
                        continue

                    uid = binascii.hexlify(tag._nfcid)
                    print "NFC detected. uid = {0}".format(uid)
                    if uid in key_uids:
                        unlock_sesame(device_id, uid)

            # 共通
            print "sleep {0} seconds".format(str(TIME_wait))
            time.sleep(TIME_wait)

        clf.close()
            if brty == "106A":
                tag = nfc.tag.activate_tt2(clf, target_res)
            elif brty == "212F":
                tag = nfc.tag.activate_tt3(clf, target_res)

の部分ですが、Windowsで行っていた際、 nfc.tag.activate(clf, target_res) でどちらも対応できましたが、
raspbian だとそれぞれTypeを指定しないと動きませんでした。

コード全体は GitHub に置いてあります

mizutoki79
Microsoft と ネットワークとセキュリティが好きなサーバーサイドエンジニア
yumemi
みんなが知ってるあのサービス、実はゆめみが作ってます。スマホアプリ/Webサービスの企画・UX/UI設計、開発運用。Swift, Kotlin, PHP, Vue.js, React.js, Node.js, AWS等エンジニア・クリエイターの会社です。Twitterで情報配信中https://twitter.com/yumemiinc
http://www.yumemi.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away