アイデンティティ
Raiznetのすべての参加者 — ユーザー、サーバー、デバイス — はEd25519鍵ペアで識別されます。中央の権威はありません。
鍵の階層
ユーザーのシードフレーズ(BIP-39、12語)
└─ ユーザー鍵ペア (Ed25519)
└─ DeviceClaim (デバイスのpubkeyに署名)
└─ デバイス鍵ペア (Ed25519、プロビジョニング時に生成)
すべてのテレメトリパケットに署名ユーザー鍵 は権威の根です。デバイスの所有権の主張やネットワークマニフェストに署名します。テレメトリの署名には決して使われません — それはデバイスの役割です。
デバイス鍵 はプロビジョニング時にハードウェアとともに生まれ、ESP32のフラッシュに存在します。デバイスが破壊または紛失されると、鍵は失われます。設計上、回復経路はありません — 新しいデバイスは新しいアイデンティティとしてプロビジョニングされます。
ユーザー鍵の生成
import { generateMnemonic, mnemonicToSeedSync } from '@scure/bip39';
import { wordlist } from '@scure/bip39/wordlists/english';
import { keyPair } from 'hypercore-crypto';
const mnemonic = generateMnemonic(wordlist, 128); // 12語
const seed = Buffer.from(mnemonicToSeedSync(mnemonic)).subarray(0, 32);
const { publicKey, secretKey } = keyPair(seed);シードフレーズは決定論的に導出されます。同じ12語は常に同じ鍵ペアを生みます。これは次を意味します。
- シードフレーズの回復はすべてのユーザー鍵を回復します。
- デバイスの対称鍵はユーザーのシード + デバイスのpubkeyから決定論的に導出できるため、シードの回復は暗号化されたすべてのテレメトリフィールドを復号する能力も回復します。
サーバーアイデンティティ
初回起動時、サーバーは新しいBIP-39シードを生成し、DATA_DIR/identity.mnemonic に権限 0600 で書き込みます。このファイルが唯一の永続的な秘密です。バックアップしてください — これはノードのアイデンティティであり、ネットワークが入った後は NetworkManifest イベント、フィルター、カタログに署名するものです。
サーバーの公開鍵は起動時にログに記録されます。
{"pubkey":"641ffb278dc6...","msg":"raiznet server started"}デバイスのプロビジョニング
セットアップ時:
- デバイスはハードウェアTRNG(32バイトの乱数)から鍵ペアを生成し、フラッシュ(NVS)に保存します。秘密鍵はデバイスから決して出ません。
- 所有者アイデンティティは、デバイスのキャプティブポータルでBIP-39ニーモニックとして生成またはインポートされます — デバイスのライフサイクル を参照してください。
- 計画中: ユーザーは
DeviceClaimイベントに署名して自身の公開イベントログに公開し、任意の読み取りが所有権チェーンに対して検証できるようにします。
リファレンスファームウェアにおける所有者鍵の導出
リファレンスファームウェアは現在、所有者のEd25519シードを、サーバー(@raiznet/crypto)が使う完全なBIP-39/PBKDF2導出ではなく、ニーモニック文字列のSHA-256 として導出します。したがって同じフレーズが2つの経路で異なる鍵を生みます。所有者シードのインポートがアプリに入る前に、正規ルールがADRで確定されます — それまでは、ファームウェアが生成する所有者鍵はデバイス内に限定されたものとして扱ってください。
所有権の移転
デバイスの販売や移転には、デュアル署名(売り手 + 買い手)の DeviceTransfer イベントを使います。両者は、デバイスのpubkey、2つのユーザーpubkey、タイムスタンプを含む同じ構造に署名します。ネットワークは有効な移転を見た後に owner_pubkey の見方を更新します。
BIP-39とバックアップ
シードフレーズはマスターシークレットです。次であるべきです。
- 紙に書いて安全な場所に保管する。
- クラウドサービスに保存したり、暗号化されていないチャンネルで送ったりしない。
- 生成時に一度だけ、確認ステップとともにユーザーに表示する。
中央集約的な回復はありません。これは根本的な設計上の選択であり、制限ではありません。
チャレンジ・レスポンス認証 計画中
ローカルエンドポイント(127.0.0.1:LOCAL_PORT)は、所有者が自身のユーザー秘密鍵の所持を証明することを要求します(まだ未実装 — ローカルAPI を参照)。
- クライアントが
GET /v1/auth/challengeを呼ぶ → 32バイトの乱数を受け取る。 - クライアントがそのバイト列を自身のユーザー秘密鍵で署名する。
- クライアントが署名を
POST /v1/auth/verifyに送る → セッショントークンを受け取る(あるいはサーバーがリクエストごとに検証する)。
これは、誰かがサーバーのローカルネットワークへのアクセスを得たとしても、所有者のプライベートデータへの不正アクセスを防ぎます。入るまで、ローカルエンドポイントは 127.0.0.1 バインドに依存します — 公開しないでください。