概要
-
nfcpyを使って簡単にFeliCaのエミュレーションをやれる - もちろんSW的なものだけで解決するわけではなく、あくまで対応するFeliCaのカードリーダーが必要になる
- まぁそんなに高いものでもないし簡単に手に入る
- これらのアプローチによってFeliCa関連の開発やら検証がだいぶ捗る
- いちいちたくさんのカードを用意して、わざわざ手動でかざして...とか時間かかるし大変だからね
- 最後に今回紹介した手法以外のアプローチについても紹介
事前に必要なもの
- 以下に記載のいずれかのカードリーダー
基本的な使い方
Getting Starged をまず読みましょう。
lsusb とかで色々確認する手順が書かれてるけど、まぁ別にやらんでも Supported devices に載ってる値を使えばOK
超単純に読み取り
以下のコードを実行し、カードをかざす。これでIDmが表示されるはず。
import nfc
def on_connect(tag):
# タグ種別やIDなど(FeliCaなら Type3Tag になることが多い)
print(tag)
# NDEFがあれば表示(なければ None)
if tag.ndef is None:
print("NDEF: (none)")
else:
print(f"NDEF length: {tag.ndef.length}")
for r in tag.ndef.records:
print(" -", r)
# False を返すと、presence loop を回さずにすぐ connect() が戻る(=1回読んで終わり)
return False
def main():
# 'usb' の部分は 'usb:054c:06c1' のように特定デバイス指定もOK
with nfc.ContactlessFrontend("usb") as clf:
tag = clf.connect(rdwr={"on-connect": on_connect})
# on_connect が False を返すと、ここに tag が返る(必要ならここで追加処理)
# print("returned:", tag)
if __name__ == "__main__":
main()
- 実行例
- スクリプト実行後、すかさずカードリーダーにカードをかざす
Type3Tag ID=01234567890ABCDEF PMM=01234567890ABCDEF SYS=0003
NDEF: (none)
超単純にエミュレート
読み取ったカードのIDmを指定してエミュレートさせる。動作確認としてスマホを使えばOK.
import struct
import nfc
# 16バイト=1ブロック。ここでは「16ブロック(=256B)」だけ用意
ndef_data_area = bytearray(16 * 16)
def init_attr_block():
# Attribute Information Block (block 0) を最低限埋める
# 公式ドキュメントの例に沿う(RC-S380前提の読み書きブロック数)
ndef_data_area[0] = 0x10 # NDEF mapping version '1.0'
ndef_data_area[1] = 12 # 一度に読めるブロック数
ndef_data_area[2] = 8 # 一度に書けるブロック数
ndef_data_area[4] = 15 # NDEFデータ用ブロック数(ここでは 16ブロック中 block0 を除く等、用途に合わせて調整)
ndef_data_area[10] = 1 # 読み書き許可
# NDEF message length (bytes 11..13) は 0 のまま(空)
ndef_data_area[14:16] = struct.pack(">H", sum(ndef_data_area[0:14])) # checksum
def ndef_read(block_number, rb, re):
if block_number < len(ndef_data_area) // 16:
i = block_number * 16
return ndef_data_area[i:i+16]
def ndef_write(block_number, block_data, wb, we):
if block_number < len(ndef_data_area) // 16:
i = block_number * 16
ndef_data_area[i:i+16] = block_data
return True
def on_startup(target):
# 例: IDm/PMm/SYS をセット(公式例の値)
idm, pmm, sys = "03FEFFE011223344", "01E0000000FFFF00", "12FC"
target.sensf_res = bytearray.fromhex("01" + idm + pmm + sys)
target.brty = "212F" # FeliCa 212kbps
return target
def on_connect(tag):
print("tag activated")
# サービスコード 0x0009, 0x000B を NDEF読み書き向けに登録(公式例)
tag.add_service(0x0009, ndef_read, ndef_write)
tag.add_service(0x000B, ndef_read, lambda: False)
return True # Trueだと「タッチされている間」動作し、離すとループが1回戻る
def main():
init_attr_block()
# RC-S380/S: usb:054c:06c1 (環境に合わせて変更)
with nfc.ContactlessFrontend("usb:054c:06c1") as clf:
while clf.connect(card={"on-startup": on_startup, "on-connect": on_connect}):
print("tag released")
if __name__ == "__main__":
main()
- 実行例: スクリプト実行後にすかさずスマホをかざす
tag activated
tag released
こんな感じで読み取れた。これはまさに↑でエミュレートさせたやつを読み込ませた。
※ iOSの NFC Tools アプリを使用
復合パターン
あとは組み合わせの問題でしかなく、例えば copyモードのように読み取ってそれをすぐにエミュレート... というスクリプトも簡単に書ける。
もっと楽なパターン
ここまでは汎用的なカードリーダーと nfcpy を使った方法を紹介したが、もっと楽なパターンもあるっちゃある。
単一HW的に全部組み込まれたものもあって、例えば Flipper Zero が一番有名だろう。Amazonから買える。
PC持ち歩いてあーだのこーだのやるのは大変。こういった小型デバイスなら、まぁ色んな環境での利用が容易になる。
詳しい言及は避けるが、やろうと思えばやっちゃいけないことにも使えてしまう。
たっけーよ!と思う人向けには、ググれば互換機がいくらか出てくる。
FW書き換えを前提にすれば数千円レベルで買えるものも全然ある。
他のNFCのタイプは??
ここまでくると、あれ? FeliCa以外はどうなるの?と当然思うだろう。
今回紹介したnfcpyを使うアプローチだとFeliCa以外は無理。
が・・・一部限定的ではあるが、上述のHWを使うようなアプローチなら可能。そのうち記事にするかもしれない。
