
【Seedsigner】NFC cardにシードを読み書きする機能を追加してみた。
こんにちはかずみょんです。NFC cardにシードを保存できたらいいなと思いseedsignerでNFC cardにシードを読み書きできる機能を実験的に実装してみました。seedsignerはいろいろ自分好みの仕様に変更できたりパーツを組み合わせたりして機能を追加できたりするのはいいですよね。 おおまかな仕様だけ考え実装はほぼAI頼みだったのですが実装過程や参考にしたサイトなどを少しここに書いてみたいと思います。
また、実際の実験において実装ではシードなどの機密性の高い情報を取り扱うためセキュリティには十分な注意が必要です。本記事はテスト及び学習目的で公開しています。
ソースコードはこちら↓↓↓
#### Seedsigner_pn532 ####
1.ハードウェア構成
NFC cardを読み書きするmodule pn532を使います。pn532とRaspi zero Wを下図のように接続する。Raspi zeroにはpin3,5のGPIOがI2C(I2C:シリアル通信の規格)バスとして割り当てられておりLCDとは接続されていないためpn532 moduleと接続が可能となっている(他にも空いているGPIOがあるので、そこにアサインすることもできそう)。ハード的には4ピン繋ぐだけなので簡単ですね。
後、pn532のVCC pinにはRaspiの5V電源を繋ぎます(3.3Vだと電圧降下が激しく動作不安定になるみたい)。
2.仕様
2-1.NFCカード
・Mifare 1K classicカードを使用
・Mifare 1K classicカード仕様
Mifare 1K classicカードは16セクタで構成されてあり、1セクタは4ブロックからなる。1ブロックは16Byte
・NFCカードのメモリ管理
NFCカードの1セクタに1シードを書き込み可能とする。
セクタの1番目のBlockを管理ブロックとする。
- [0:3]byte : 有効セクタかどうかを判断する。 "AA55FF00" : 有効セクタ, それ以外 : 無効セクタ
- [4] : 128bitシードか256bitシードかを判断。 "00" : 128bit, "FF" : 256bit
- [5:6] : checksumを保存(128bitの場合12word目、256bitの場合24word目)
- [8:15] : Fingerprint書き込み用
セクタの2,3番目のBlockに圧縮したシード(エントロピ)を書き込む。
- 128bitの場合: 11bit x 11word
- 256bitの場合:11bit(前半1ブロックのみ使用) x 23word(2ブロック使用)
2-2.NFC card書込みルーチン概要:
- セクタを順番に管理ブロックデータを読み込む
- 管理ブロック(セクタ×4+1)データを読み出し無効セクタかどうか確認する。[0:3]byte != "AA55FF00" をチェック
- ①無効セクタの場合2,3番目blockにシード書き込み(128bitシードの場合は2番目のみ)---->> go to 4.
②無効セクタでない場合次のセクタへ行く。---->> go to 1. - 1番目blockに管理データ書き込み
2-3.NFC card読出しルーチン概要:
カードに書き込まれている有効なシード一覧の読み込みを行い、有効なシード一覧(Fimger print)を表示する。対応するセクタ番号も記憶しておく。シードを選択しseedsignerのメモリに読み込む。
- セクタを順番に(4block分)データを読み込む。
- 管理ブロック(セクタ×4+1)データを読み出し有効セクタかどうか確認する。[0:3]byte != "AA55FF00" をチェック
- 有効セクタでない場合無視して次のセクタへ行く。 ---->> go to 1.
- すべてのセクタを読み込み有効なシード一覧(Fimger print)を表示
- 有効なシード一覧からシード選択
- settings -> Advanced -> BIP39 passphrase が "reauired"の場合はpassphraseを入力させる。
- 管理ブロックの128bitシードか256bitシードか確認 シード(11word or 23word分)から算出されたshecksumが管理ブロックのものと同じかどうかチェックを行いseed(passphraseがあればそれを加味)をメモリーに加える。
2-4. その他
NFCカードの一般的な使用については自分はよく分かっていない。認証やエラーチェックについてはあまり考慮していないと思うので知見のある方はいろいろ教えてほしいです。
3.コードの実装
コード実装は仕様をベースにほぼAI(Github copilot)を使って書かせました。Debug自体はRaspi zeroにローカル開発環境をインストールし動作確認を行う。
3ー1.Raspiに開発環境をインストール
Makoto Ishidaさんのこの記事を参考にローカル環境をインストール ↓↓↓SeedSignerをいじってみる - メニューの色をお好みに
基本はこの記事通りやればできるのですが、自分の環境では少し問題あったりしたので再度繰り返しになりますが手順を書いておきます(ほとんど上記記事と同じ内容になります)。
①Raspi OSをインストール
raspi OSはこれを使用 → raspios_lite_armhf-2023-05-03
SD cardへの書込みにRaspberry Pi imagerを使用 → Raspberry Pi imager
Raspberry Pi imagerを起動しSD cardへ書き込み
通常これでRaspi zeroにSD cardを指し込み電源いれれば、Wifiに接続されるはずなのだが上手くいかなかった。これはRaspberry Piの起動時に、自動ログインがうまく機能しておらず、ログイン前にWi-Fi設定処理が走らなかったようだ。
なので、SD card内のrootパーティション内の/etc/shadow(Linuxのパスワード情報を管理するためのファイル)を直接編集することで、初回ログイン時にパスワードなしでログインできるようにした(piの覧を下記に変更)。ちなみにwindowsではフォーマットが違うためrootパーティションは見えない。
pi::19480:0:99999:7:::
これでWifiにつながるようになるはず、端末からログインしRaspi zeroにアクセスできるようになる。
②Raspi起動と設定
SSHでログイン、私はWindowsでPuTTY(Windows上で動作するSSH)を使ってます。
以下でシステム設定(I2CをEnableにしましょう)
sudo raspi-config
- Interface Options → Camera: Enable
- Interface Options → SPI: Enable
- Interface Options → I2C: Enable ← 追加
- Localisation Options → Locale: en_US.UTF-8を選択
③コンパイル用ツールとPython3.10のインストール
✅ 1. コンパイル用ツールをインストール
sudo apt update && sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev openssl libssl-dev libreadline-dev libffi-dev wget libsqlite3-dev
✅ 2. Python 3.10のソースをダウンロード
cd ~
wget https://www.python.org/ftp/python/3.10.10/Python-3.10.10.tgz
tar -xzvf Python-3.10.10.tgz
cd Python-3.10.10
✅ 3. 設定(Makefaile等生成)
./configure --enable-optimizations
✅ 4. コンパイル
sudo make altinstall
✅ 5.依存ライブラリのインストール
sudo apt install -y python3-pip python3-pil libjpeg-dev zlib1g-dev libopenjp2-7 git python3-opencv python3-picamera libatlas-base-dev qrencode
✅ 6.QRコード読み取り用ライブラリ(zbar)
curl -L http://raspbian.raspberrypi.org/raspbian/pool/main/z/zbar/libzbar0_0.23.90-1+deb11u1_armhf.deb --output libzbar0_0.23.90-1_armhf.deb
sudo apt install ./libzbar0_0.23.90-1_armhf.deb
✅ 7.Raspberry Pi GPIO制御用ライブラリ(BCM 2835)
wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.60.tar.gz
tar zxvf bcm2835-1.60.tar.gz
cd bcm2835-1.60/
sudo ./configure
sudo make && sudo make check && sudo make install
④カスタムseedsignerのセットアップ
✅ 8.カスタムseedsignerをクローン
cd ~
git clone https://github.com/kazuW/seedsigner_pn532.git
✅ 9.Python 3.10の仮想環境と必要モジュールを構成
python3.10 -m venv venv
source venv/bin/activate
pip install --upgrade pip setuptools
pip install -r requirements.txt
pip install -r requirements-raspi.txt
pip install adafruit-blinka
pip install adafruit-circuitpython-pn532
4.動作確認
後は、実行するだけです。
python ./src/main.py
これで動くはずです。
4-1.NFCカードへシード書き込み
シードの書き込みは最初にセッティングからNFC exportをEnableにする必要があります。その後シードを選択し "Backup seed" -> "Export NFC"へ進んでください。以下手順です。
4-2.NFCカードからシード読み出し
シードの読み込みは1回カード内のシードをすべて読み出します。その後一覧が表示されるのでLoadしたいシードを選択しseedsignerのメモリ内へLoadしてください。
4-3.その他
NFC cardへの読み出し/書き込みは結構不安定なところあるので、一回失敗しても何回か試してやると成功したりします。
コードの変更部分は仕様をベースにした画面遷移と画面遷移した時の処理をAIに指示して変更させることで対応しました。また、SeedSignerのLCDのピン設定をBCMで指定しないとPN532の機能が動作しないことがわかりLCDライブラリの設定もBCM番号に変更した。
”””SeedSignerのLCD制御でBOARD番号(物理ピン番号)を使用、PN532ライブラリ(nfcpy等)がBCM番号(GPIO番号)を前提として動作、同じGPIOピンに対して異なる番号体系でアクセスしようとして競合していた。”””
また、現状は生エントロピを保存してるので暗号化して保存するとかすれば、もう少しセキュリティとかはよくなると思う。
5.まとめと感想
SeedSignerとNFCカードを用いて、シード情報(エントロピー)をNFCカードに保存/読み出しする機能を実験的に実装してみた。ハードはRaspberry Pi Zero WとPN532 NFCモジュールを使い、SeedSignerの機能拡張として動作確認した。 コード実装は主にAI(GitHub Copilot)を活用し、Raspi上にローカル開発環境を構築してデバッグを行った。
まあシードをどこに保存するかという処々の問題はあるが、seedsignerでいろいろ自分仕様にカスタマイズできておもしろかった。またコードを理解することでいろいろ勉強になることはあったという感じです。
最後に繰り返しになりますが、このプロジェクトは テストおよび学習目的で公開しています。セキュリティに関する十分な理解がない状態での実際の資産管理への使用はしないでくださいね。
ではまた。