久しぶりにmacにてnfcpyを利用してFeliCa(WAON)を読む機会があったのでメモ。
以前(2016年)にも同じ趣旨の記事を書いたことがあったのですが、2021年環境で再挑戦。
2016年との同じところ・違うとこ
2016年と同じところもあれば、違うところもある。基本的に簡単になってます。
- pipはbrewでは(あいかわらず)インストールできない
- nfcpyはpython3.x(3.5以上)にも対応した(2.xじゃないとダメって記事が多かったけど3.xでOKみたいです)
- インストールする周辺モジュールは減った(でも0ではない)
やりたいこと
そもそもやりたいことは以下の通り。システムコードやサービスコードがわかっている(わからなくても調べられる)サービスであればいろいろ応用はできると思います。
- Mac + Pasori(RC-S380)を利用したい
- あるFeliCa(WAON)のメモリ構造と内容を知りたい
- WAON番号(16桁)を取り出したい
- 最終的にはラズパイで利用したのですが、ここではまずnfcpyの利用を検証
インストール
では、環境整備から。
pip
何はなくてもpip。Macにはpython2とpython3がインストールされていますが、python3を明示的に利用してみます。
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python3 get-pip.py
そもそもget-pip.pyはpython3用のコードらしく2だとエラーになりましたね。
Nfcpy
pip3 install nfcpy
libusb
libusbが無いと実行時にエラーになるみたいなのでbrewでインストールしておきます。
brew install libusb
lsusb(おまけ)
そもそもMacにPasoriが認識されているかを調べたいのでインストールしておきます。
brew install lsusb
lsusb
Bus 020 Device 001: ID 05ac:0267 Apple Inc. Magic Keyboard Serial: F0T733400FKJ20TAG
Bus 020 Device 000: ID 05ac:8294 Apple Inc. Bluetooth USB Host Controller
Bus 020 Device 019: ID 05ac:8511 Apple Inc. FaceTime HD Camera (Built-in) Serial: CCGH17035YH0G1F0
+Bus 020 Device 005: ID 054c:06c3 Sony Corporation RC-S380/P Serial: 0523593
Bus 000 Device 001: ID 1d6b:ISPT Linux Foundation USB 3.0 Bus
認識されていますね。
テスト1:メモリタンプ
WAON番号を読むためのシステムコードやサービスコードの情報はネット上にありますが、他のエリアの内容も見たかったのでメモリダンプしてみます。
実装
先人のノウハウがあったので参考にさせていただきました。本当にありがとうございます。
ほぼpython2を3にしているだけです。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import nfc
def connected(tag):
# IDm, PMM等を出力
print(tag)
if isinstance(tag, nfc.tag.tt3.Type3Tag):
try:
# 内容を16進数で出力する
print((' ' + '\n '.join(tag.dump())))
except Exception as e:
print("error: %s" % e)
else:
print("error: tag isn't Type3Tag")
# タッチ時のハンドラを設定して待機する
clf = nfc.ContactlessFrontend('usb')
clf.connect(rdwr={'on-connect': connected})
実行
実行してみます。
ポイントはNDEF用のSystem 0x12FCと共通領域(0xFF00)の2つのシステムが存在しているということ。
で、WAONの情報は0xFF00の方に書かれています。
python3 waon.py
Type3Tag 'FeliCa Standard (RC-S962)' ID=0114F100A0143000 PMM=0120220427674EFF SYS=12FC
System 12FC (NDEF)
Area 0000--FFFE
System FE00 (Common Area)
Area 0000--FFFE
Area 3940--39FF
Area 3941--39FF
Random Service 229: write with key & read w/o key (0x3948 0x394B)
0000: 70 ed 8e aa 14 10 00 70 01 00 00 00 00 00 00 00 |p......p........|
Random Service 230: write with key & read w/o key (0x3988 0x398B)
0000: 50 75 bc 5a 48 23 68 c5 dc b6 f5 0c c0 c2 ce 71 |Pu.ZH#h........q|
0001: 89 ee 71 e6 12 3b be ff 96 b5 2b 69 1a 54 1e f1 |..q..;....+i.T..|
0002: 47 76 f7 10 bb 9f 3d ec 92 86 53 f1 43 11 07 cc |Gv....=...S.C...|
0003: fc db 41 9e cd 36 7e 33 1d 4b 69 24 c2 65 a6 36 |..A..6~3.Ki$.e.6|
0004: d6 34 2c 24 02 0e bf c3 f7 5d 89 32 9f 2c ba 48 |.4,$.....].2.,.H|
0005: d7 78 f1 50 56 31 d5 84 d8 22 b8 2d 93 1a e9 27 |.x.PV1...".-...'|
0006: 31 c9 5c f2 46 c8 41 3c 58 77 87 ca 7d 05 69 53 |1.\.F.A<Xw..}.iS|
0007: a8 3f f1 e9 f4 fb 5b 2d f4 49 a9 47 31 fd a3 fc |.?....[-.I.G1...|
0008: ae 0e 81 cf bf 1b 37 12 a3 4d 53 f4 25 c8 95 00 |......7..MS.%...|
0009: 09 88 52 7c 78 07 76 64 66 7c 1f 05 77 16 f9 a1 |..R|x.vdf|..w...|
000A: fe 1f 3f 59 5b d5 7f 7e 2d 63 7e 8e 9b 99 8f db |..?Y[..~-c~.....|
000B: 1a 15 f1 9f 5b f3 2b 6a 18 28 97 55 cd b6 ad 66 |....[.+j.(.U...f|
000C: 84 16 0f 5e fb 4f c6 01 7a 20 35 08 75 b7 30 01 |...^.O..z 5.u.0.|
000D: 37 56 9a 95 10 90 4f e0 36 82 df 49 80 22 6e 2e |7V....O.6..I."n.|
000E: 49 58 ab c3 32 92 ee 5d 3b c1 33 e9 4f 59 04 79 |IX..2..];.3.OY.y|
000F: 1b 5d 05 38 3b 43 7d 9f ff a2 d3 a2 01 11 cf 29 |.].8;C}........)|
0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0026: 09 88 52 7c 78 07 76 64 09 88 52 7c 78 07 76 64 |..R|x.vd..R|x.vd|
0027: 09 88 52 7c 78 07 76 64 32 53 fe 51 a6 0c 7b 31 |..R|x.vd2S.Q..{1|
Random Service 231: write w/o key (0x39C9)
0000: 00 00 dd 00 00 2e e0 00 00 2e e0 00 00 c9 00 00 |................|
0001: 00 1e 00 00 00 1e 00 00 e9 00 00 00 05 00 00 00 |................|
0002: 05 00 00 e4 00 00 00 00 00 00 00 00 00 00 e8 00 |................|
0003: 00 00 00 00 00 00 00 00 00 56 00 00 00 01 00 00 |.........V......|
0004: 00 01 00 00 bb b6 2d 13 b3 b6 3c 55 e8 00 00 f2 |......-...<U....|
0005: 00 00 00 32 00 00 00 32 00 00 00 00 00 00 00 00 |...2...2........|
0006: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000E: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
Area 67C0--68FF
Area 67C1--68FF
Random Service 415: write with key & read w/o key (0x67C8 0x67CB)
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0001: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 41 |...............A|
0002: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0003: a9 22 ee a6 a8 e1 0a 89 80 00 00 00 00 00 00 00 |."..............|
Cyclic Service 415: write with key & read w/o key (0x67CC 0x67CF)
0000: 00 01 01 01 00 01 01 01 00 01 00 c0 69 00 17 21 |............i..!|
0001: 31 95 12 43 00 00 00 00 00 00 00 00 00 00 00 00 |1..C............|
0002: 00 00 00 00 00 00 00 00 50 00 00 00 00 00 00 00 |........P.......|
0003: 0c 0f e2 92 f5 e1 89 e1 e1 40 28 03 51 19 a9 e0 |.........@(.Q...|
0004: a4 15 b3 e0 0d a8 d6 d5 f6 fb 60 1c 2a b1 c8 45 |..........`.*..E|
0005: 47 1c 5d e7 99 49 e0 71 1f 9b f4 6b ac f2 2e 93 |G.]..I.q...k....|
0006: d4 56 c7 22 87 fc 2b 01 c5 12 28 db bf 49 10 e8 |.V."..+...(..I..|
Random Service 416: write with key & read w/o key (0x6808 0x680B)
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0007: 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 |....@...........|
0008: 00 c9 1b f8 2a 58 ed e0 80 00 00 00 00 00 00 00 |....*X..........|
Cyclic Service 416: write with key & read w/o key (0x680C 0x680F)
0000: 39 00 e3 45 0f 46 89 2d 25 8d a5 39 13 e7 a7 35 |9..E.F.-%..9...5|
0001: 4e 65 9f 1b f5 e1 fe 0f cc d8 a0 84 04 0f c2 99 |Ne..............|
0002: d9 57 42 1d d0 0b 52 c8 54 90 22 d7 22 2c 21 bf |.WB...R.T.".",!.|
0003: d0 48 92 89 78 11 08 f2 df ab f7 f3 57 81 c6 74 |.H..x.......W..t|
Purse Service 416: direct with key & decrement with key & read w/o key (0x6810 0x6814 0x6817)
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
Random Service 417: write with key & read w/o key (0x6848 0x684B)
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0002: 13 33 ab 4a b3 1b d4 a9 80 00 00 00 00 00 00 00 |.3.J............|
Cyclic Service 417: write with key & read w/o key (0x684C 0x684F)
0000: 69 00 17 21 31 95 12 43 01 00 00 00 00 00 00 00 |i..!1..C........|
0001: ce 92 1a d2 38 dc c6 16 80 00 00 00 00 00 00 00 |....8...........|
Random Service 418: write with key & read w/o key (0x6888 0x688B)
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0002: 29 a1 33 1e e3 9d a2 50 80 00 00 00 00 00 00 00 |).3....P........|
Cyclic Service 418: write with key & read w/o key (0x688C 0x688F)
0000: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 |................|
0001: 86 a1 a0 e8 21 ad 9d 06 80 00 00 00 00 00 00 00 |....!...........|
Random Service 419: write with key & read w/o key (0x68C8 0x68CB)
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0002: ac ea 20 3b c1 52 16 c7 80 00 00 00 00 00 00 00 |.. ;.R..........|
Cyclic Service 419: write with key & read w/o key (0x68CC 0x68CF)
0000: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 |................|
0001: 82 77 e7 99 2d 9d 7c 92 80 00 00 00 00 00 00 00 |.w..-.|.........|
WAON番号を探す
WAON番号はWAONカードの裏面に刻印されている16桁の番号です。
WAON番号の位置が知りたかったのですが、どうやら下記の2箇所にかかれているようです。
変換無し16進数表記なんですよね(苦笑)。
サービスコード0x67CFの
block0の12,13,14,15バイトと(0スタート)
block1の0,1,2,3バイト(0スタート)目の9バイトに書かれてるのと
サービスコード0x684Fの
block0の0,1,2,3,4,5,6,7バイト目までに16進数表記で書かれているようです。
0x684Fのほうが1ブロックにかかれているから利用しやすい感じなので、そちらを利用してみます。
テスト2:WAON番号だけ取り出す
では、上記ダンプ情報を参考に、WAON番号を取り出してみます。
実装
ポイントはシステムコードを指定しているところです。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import nfc
#WAONサービスコード
service_code = 0x684F
def connected(tag):
#システムコード指定(指定しなければ最初に見つかった12FCが読まれる)
idm, pmm = tag.polling(system_code=0xfe00)
tag.idm, tag.pmm, tag.sys = idm, pmm, 0xfe00
if isinstance(tag, nfc.tag.tt3.Type3Tag):
try:
#サービスコードの指定
sc = nfc.tag.tt3.ServiceCode(service_code >> 6 ,service_code & 0x3f)
#読取りブロックを指定(複数読むときは配列で指定する)
bc = nfc.tag.tt3.BlockCode(0,service=0)
data = tag.read_without_encryption([sc],[bc])
#読む位置を指定:0から8byteまでを読んで16進数表記
print(data[0:8].hex())
except Exception as e:
print(("error: %s" % e))
else:
print("error: tag isn't Type3Tag")
# タッチ時のハンドラを設定して待機する
clf = nfc.ContactlessFrontend('usb')
clf.connect(rdwr={'on-connect': connected})
(servc >> 6, servc & 0x3f)は上位10bitをサービスコード、下位6bitをアトリビュート値して抽出して利用している。サービスコードを直接渡したい・・・。
実行
では実行。
python3 waonno.py
6900172131951243
システムコードを指定せずに実行したら error: invalid service code number or attribute ってエラーがでます(それは12FCにはそんなサービスコードは無いから)。
メモ
python2からpython3へのコード変換
python2からpython3へのコード変換は以下のコマンドでできます。
#上書き
2to3 -w test.py
Raspberry Piで利用する場合
原則、同じコード(概念)で動くが、いくつか注意が必要
- なぜかnfcpy(nfc)モジュールを読み込んでくれないのでPathを指定
- USBは具体的に指定。'usb'ではなく、'usb:054c:06c3'などとする。
あとは同じで動きました。
~
import sys
sys.path.append('/home/pi/.local/lib/python3.7/site-packages')
import nfc
#HID Keyboard
from Py_Keyboard.HID import Keyboard
kbd = Keyboard()
#WAONサービスコード
service_code = 0x684F
def connected(tag):
#システムコード指定(指定しなければ最初に見つかった12FCが読まれる)
idm, pmm = tag.polling(system_code=0xfe00)
tag.idm, tag.pmm, tag.sys = idm, pmm, 0xfe00
if isinstance(tag, nfc.tag.tt3.Type3Tag):
try:
#サービスコードの指定
sc = nfc.tag.tt3.ServiceCode(service_code >> 6 ,service_code & 0x3f)
#読取りブロックを指定(複数読むときは配列で指定する)
bc = nfc.tag.tt3.BlockCode(0,service=0)
data = tag.read_without_encryption([sc],[bc])
#読む位置を指定:0から8byteまでを読んで16進数表記
no = data[0:8].hex()
print(no)
#kbd.write(no+"\n")
except Exception as e:
print(("error: %s" % e))
else:
print("error: tag isn't Type3Tag")
# タッチ時のハンドラを設定して待機する
clf = nfc.ContactlessFrontend('usb:054c:06c3')
clf.connect(rdwr={'on-connect': connected})
実行時はsudoで。
sudo python3 waonno.py