あなたの乱数は大丈夫?Not Your 乱数, Not Your Coins!🎲#61
こんにちは、もやしししゃもです。お久しぶりです。
先月はBitcoin ETFが盛り上がっていましたね。あと2ヵ月もすれば半減期があり、上昇相場を期待するような記事も出てきています。
そんな中忘れがちなのが、Bitcoinの安全な保管です。昨今、ウォレットがハッキングされたとか、シード生成に脆弱性があったとか、怖い話を見かけることがあります。金額が膨らめば膨らむほど、ウォレット周りの恐怖心は心の隅で大きくなっていきます。
エントロピーが低いらしい
Libbitcoin Explorerのmilk sad脆弱性
特定バージョンでシードを出すと必ず「milk sad…」で始まるシードフレーズが出力されるとのこと
上記2つとも、弱いエントロピーによって弱い秘密鍵ができて、お金が盗まれてしまう脆弱性の内容です。
上記の脆弱性は専門家に任せて、気になるのは「自分の秘密鍵は大丈夫か!?」「自分のウォレットは大丈夫か!?」というところですね。
僕も、不安になりつつも一人で盛り上がっていました。下記画像のXスレッドでは、僕が安全な秘密鍵を目指して色々試した形跡が残っています。
今回の記事では、乱数と秘密鍵の関係の説明から、Bitcoinウォレットの「ColdCard」のサイコロ乱数機能をVerifyするところまでやろうと思います。
「どうやってシードフレーズが生まれるの?」「ColdCardを信頼し過ぎて怖い!」「盗まれてないか心配で毎日アドレスの残高を見てる」みたいな感じで、Bitcoinに興味がある人にとっておもしろい内容かと思います。
ディープな内容ですが、Bitcoinに興味を持ち始めたばかりの方にとっても非常に重要な内容だと思います。
また、僕はしっかりコードを読めるわけではないので、「プログラミングよくわからん」という人にもわかりやすいかと思います。(もし間違ってることがあれば教えてください。)
※ 本記事に出てきたシードフレーズやアドレス、あなたが検証で使ったシードフレーズは絶対にウォレットとして使用しないでください。資金を失う可能性高いです。
↓LNでのチップはこちら(1satoshiに感謝⚡)
moyashamo@getalby.com
秘密鍵における乱数の重要性🎲
「秘密鍵」とか「シードフレーズ」は聞いたことがありますが、「乱数」はあんまり聞いたことがないですよね。実は、秘密鍵もアドレスも乱数から始まっているのです。乱数は「種」のようなもので、成長すると葉や花ができるように、秘密鍵や公開鍵、アドレスなどが作られます。
下図が、乱数からシードフレーズができるまでの道のりです。
簡単に説明すると、サイコロでも、コイントスでも、乱数生成器でもいいので0と1の256桁の乱数を作ります。その乱数を元に、シードフレーズを作り出せるわけです。
つまり、最初の乱数「2進数256桁」がわかると「シードフレーズ」を導くことができます。。乱数はシードフレーズと同等の価値があります。(この場合はシードフレーズから乱数も可能)
詳細な説明をすると、8桁のチェックサムにより正しいシードフレーズの形式となります。つまり、テキトーに24単語を選んでもアドレスを作ることはできません。
また、264桁にする理由は、264÷24=11となり、11桁で1単語を表現できます。BIP39のワードリストは2048(=2^11)種類あり、11桁の2進数ですべて表現できます。
例えば、00000000011だと10進数では3なので、BIP39の4番目の「about」を指します。0を1番目、1を2番目…2047を2048番目、と1つずれるので最初は混乱するかもです。
乱数とシードフレーズの繋がりの強さがわかったと思います。次は乱数が弱いとどうやばいのかを示します。
図を見てわかるように、よわよわ乱数だと出力されるシードフレーズの種類も少なくなります。よわよわ乱数は「エントロピーが弱い」という状況です。
256ビット = 2^256 = 1.1579209e+77
128ビット = 2^128 = 3.4028237e+38
10ビット = 2^10 = 1024256ビットの総当たりはスパコンで数兆年かかると言われてますが、10ビットなら一瞬です。
→2の256乗がいかに馬鹿デカいか
ウォレットアプリを入れて「バラバラで良い感じのシードフレーズだ!」と思って使っていたら、実はよわよわ乱数で作られたフレーズである日盗まれてしまうかもしれません。シードフレーズから乱数を導けますが、その乱数が弱いかどうかはアプリのコードを見ないといけません。検証には骨が折れます。
例えば次の乱数を見てください。
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
→SHA256にて空文字で作った値(「イサビ(e3b)」と覚えています)6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b
→SHA256にて「1」で作った値
一見、強そうな乱数に見えますが、SHA256に慣れ親しんだ住人であればよく見る乱数かもしれません。これを元にして作ったアドレスに入金すると即資金が抜かれることでしょう。
このように、「乱数」は秘密鍵並みに重要でありながら、アプリやウォレットで自動出力したものを利用して、それを信頼してしまっていることが多いです。
そこで僕は思いました。
Not Your 乱数, Not Your Coins.
あなたの乱数でなければ、あなたのコインではない
— Andreasさんリスペクト
「自分の鍵だ!」と自覚していても、その鍵の乱数が弱いと自分のコインではないかもしれません。
やっぱり、自分の手で生み出した乱数で秘密鍵やアドレスを作りたいよねというところです。(あとがきで書きますが、手計算はしんどくて諦めました。笑)
とはいえ、以前僕はColdCard(以下、CCと略す)とサイコロを使ってシードフレーズを作ったことがあります。「なんだ~。すでに、サイコロ乱数で作ってるじゃないか」と思いました。
しかし「いや待てよ、あれは本当に僕が作った乱数なのか…?乱数を作った感を出して、CCが良さげな乱数を入れてるだけの可能性もあるのでは…?」と疑心暗鬼になり始めました。
自分の親が本当に自分の親なのか不安に思う感覚に近いです(人による)。たしかにコードを見たらわかるかもしれないが、コードを読めるわけでもないし、実機がコードの通りに動かない可能性も0ではない。乱数部分にバックドアがあるかもしれない。。。
Bitcoinにおいて重要な言葉は「Don’t Trust, Verify.(信頼するな、検証せよ)」です。これはColdCardのサイコロ乱数について、この手で検証するしかありません!
そう思って軽く調べると、次のような記事が。。
→Verifying Dice Roll Math
まさかの、ColdCard公式がサイコロ乱数の検証方法の記事を公開しています!wこれは今日の僕みたいな悩めるColdCardユーザーのために書かれた記事です。
これを見たら検証するしかないですね。今、ここで検証します!
ColdCardのサイコロ乱数の検証🎲
さて、ここからが検証パートです。何を検証するかというと次の通りです。1つ目がメインで、残りの2つは追加で気になった点です。
ColdCardにて、自分で作ったサイコロ乱数で秘密鍵が生成されているか。
余計な乱数は入っていないか。
エントロピーは充分か。
もし、お手元にColdCardがある方は真似して検証いただいて大丈夫です。ただし、元のシードを一旦削除していないとできないので、利用しているシードフレーズであれば必ずメモした上で削除してください。
また、注意として、この検証で使った乱数、秘密鍵、アドレスは絶対にウォレットとして使用しないでください。資金を失う可能性が高いです。
ちなみに、ColdCardは下記バージョンのファームウェアを利用しています。
5.2.2(2023-12-21)
では、さっそく下記記事も見つつサイコロ乱数を検証していきましょう!(とはいえ、ほぼ記事を使わないです)
→Verifying Dice Roll Math
検証スタート
まず、PINで入り、一番上の「New Seed Words」を選択。(すでにシードが入ってる場合は無いかと思います)
次に「24 Word Dice Roll」を選択。すると下記の画面になります。(「24 Words」ではないですよ)
「0 rolls」の下に文字列がありますね。勘の良い人ならわかるかもしれませんが、SHA256にて空文字で作った値があります。
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
→SHA256にて空文字で作った値(「イサビ(e3b)」と覚えています)
戻ってまた見ても、ずっと「イサビ」です。ここから立つ仮説は「空文字の状態にサイコロ乱数を加えていくのだな」ということです。
次の数字列は、実際に僕がサイコロを転がして出した乱数です。ちなみに、ColdCardでは24wordの場合99回以上じゃないと先に進めないため100回にしてます。
サイコロ乱数(100回)
3513114212436426366326236124641321143325632342133312231513316233442422245112514316266651651614544256
同じものを入力すると、次のようなSHA256の文字列になっているはずです。(5個ずつ打ち込むのがオススメ)
100 rolls
6801adae726b9b3f9b33bf33fed1e879b9342424614b8649aea73680ca2f57a6
ここで、この文字列がサイコロ乱数を元にしたものか確かめましょう。下記のサイトに「3513114212436426366326236124641321143325632342133312231513316233442422245112514316266651651614544256」を入力して実行してみましょう。(左のText, Bin, Hexは、Textでokです)
→sha256algorithm
すると、同じ「680…」の文字列が表示されます。
この文字列の元にちゃんと、サイコロ乱数が使われている!
ここで立つ仮説として、先ほどはいきなり2進数を作ってシードフレーズを作成する説明をしましたが、CCではSHA256の結果を2進数化して使ってるかもです。(後ほど図解します)
とにかく「680…」の文字列を2進数化しましょう。
→Hex to Binary converter
0110100000000001101011011010111001110010011010111001101100111111100110110011001110111111001100111111111011010001111010000111100110111001001101000010010000100100011000010100101110000110010010011010111010100111001101101000000011001010001011110101011110100110
そしたら、この2進数を使ってWeb上のニーモニック変換器の「Entropy」に入れてみましょう。(検証で使ってるだけなので、本番ウォレットなどでWeb上のものは絶対に使わないでください)
→Mnemonic Seed
すると、次のような24wordが出力されます。
gym aspect high tooth rifle panther holiday jewel crop walk key vibrant nation bamboo muscle city main estate prefer home arrow bless future shoe
これも合ってるか心配な人は、二進数を11桁ずつに分解して自分で確かめましょう。最初の11個は「01101000000」で10進数だと832、つまり833番目のBIP39はちゃんとgymになってます。
では、ようやくColdCardの方でも24wordを作ってみましょう。並べると次の通りです。
gym aspect high tooth rifle panther holiday jewel crop walk key vibrant nation bamboo muscle city main estate prefer home arrow bless future shoe
やった、全く同じだ!つまり仮説も正しかったです。
検証のまとめ
無事、自分のサイコロ乱数からシードフレーズを導くことができました。しかも、サードパーティのWebアプリを使って一致したということは、ColdCardの恣意的な作用も働いていません。
ColdCardで乱数からシードフレーズを作る過程をまとめると次のようになります。
まず、サイコロ乱数「3513…」をいきなりSHA256に突っ込んでるのが特徴です。これにより、サイコロを100回しても200回しても同じサイズの乱数を作れます。一方、サイコロ乱数が何かは不可逆で導出できないので、自分のサイコロ乱数が何か知りたければメモするしかありません。
あとは、先ほど説明した下図と同じです。
つまり、検証の結果は下記になります。
[完了] ColdCardにて、自分で作ったサイコロ乱数で秘密鍵が生成されているか。
自分のサイコロ乱数で秘密鍵ができている
自分のパスフレーズも入れたら最高ですね
[完了] 余計な乱数は入っていないか。
余計な乱数はない
エントロピーは充分か。
エントロピーの検証
最後にエントロピーの検証をします。
ColdCardでは24wordの場合99回以上でないとシードフレーズを作成できません。理由としては、サイコロ1回は約2.585ビット(log2(6))のエントロピーがあり、ぴったりフレーズの256ビットのエントロピーを満たすには 256÷2.585=99.03、つまり99回以上が好ましいです。
厳密に計算してみます。すると、若干6^99は2^256より小さいです。そのため、不安な方は100回以上がおすすめです。
6^99=1.0888644e+77
6^100=6.5331862e+77
2^256=1.1579209e+77
結局256ビットに押し戻されるので、1000回やっても100回やるのと変わらないかと思います。ただ、99回と100回だと100回の方が安全性が高いです。
ちなみに、12wordの場合は半分の128ビットなので、サイコロ回数も約半分の50回が最低限となります。12wordも十分大きいかと思いますが、24wordに比べ圧倒的に弱いです。
2^128 = 3.4028237e+38
よって、ColdCardでサイコロする場合、現verでは99回以上でしか作れないのでエントロピーとして充分と言えます。
「24 Words」でのサイコロ乱数追加はどうなの?🤔
先ほどの検証では「24 Word Dice Roll」を選択しましたが、「24 Words」に(4)ボタンを押してサイコロ追加もできます。
では見てみましょう。すると「e58f…」です。(きっとあなたのものは違う乱数のはず)例の「イサビ」ではないので、やっぱり既に乱数が入っていますね。
ColdCardのドキュメントを見ると、「TRNG」という物理的なノイズを測定して乱数を作る内蔵の真性乱数生成器を利用していて、そのTRNGにさらにサイコロ乱数を加えられます。
→Where does the entropy (randomness) come from?
TRNGが既にあるため「24 Words」ではサイコロ回数は0回でも1回でもシードフレーズを生成できます。
「24 Words」と「24 Word Dice Roll」の比較イメージは下記です。
GitHubを見ると、TRNGが壊れてたらシードフレーズを作れないみたいなことが書いてありましたので大丈夫かと思いますが、「TRNGすら信頼できん、自分の手だけで乱数を作りたい!」という人は「24 Word Dice Roll」がおすすめです。
一方「サイコロに偏りあったらどうしよう、自分のサイコロ乱数でTRNGにアクセントを付けたい」という人は「24 Words」にサイコロを加えるのがおすすめですね。もちろん「24 Words」単体でも機能します。
個人的には、「24 Words」の方はTRNGの値がわからず検証できないので、検証できた今となっては「24 Word Dice Roll」が好きですね。
これにて、検証完了です。「シードフレーズ出せたけど、アドレスは本物?」と不安に思う人がいれば、ぜひこの先はご自身でVerifyチャレンジしてみてください。
(裏話)紙とペンとサイコロでできるか🖊️
元々、ColdCardすら使わずに、紙とペンとサイコロでシードフレーズを作ったり、アドレス作れないかと思っていました。電気や特定のマシンがない状態でも、Bitcoinを受け取ることができたらとても魅力的ですよね。さらに機械を使わないので、超コールドウォレットとも言えそうです。
ただ、キツすぎて断念しました。
1つ目はシードフレーズ出力時にチェックサムのためにSHA256を1回する必要があります。今回の検証でお世話になった下記サイト、このフローを手計算するとなると絶対計算ミスするなと思いました。
でも、SHA256を手計算でしてシードフレーズを作る人は世の中にいます。世界は広いと思いましたが、使いやすい乱数を選んでそうです。それくらい大変なのだろう。。
→SHA 256 from scratch with pen and paper
2つ目は、シードフレーズができた後にアドレスを作る方法です。HMAC-SHA512なるものが出てきて、不勉強ながら手作業でやるイメージが全く浮かび上がってきませんでした。。
今回の検証のおかげで、サイコロ乱数さえ作れば、ColdCardでシードフレーズが作れ、(アドレスまで検証してないが過去出金できたので)アドレス生成までセットでできるのでColdCard良いなと思いました。しかもエアギャップ。
紙とペンとサイコロでシードフレーズやアドレスを作ってるみたいな人がいたら、ぜひやり方教えてください。
最後に:あなたの秘密鍵はあなたの乱数ですか?🔑
「Not Your 乱数, Not Your Coins.」を確かめるべく、ColdCardにて本当に自分の乱数でシードフレーズを作れるのかを検証しました。
検証の結果、サイコロ乱数から正しいシードフレーズが作れたので、「24 Word Dice Roll」を使えばウォレットの乱数生成器のエントロピーの弱さに怯える日々は終わりを迎えます。
「Don’t Trust, Verify」とは言うけど、なかなか真剣に検証したことがなかったので、とても良い検証機会になりました。僕のColdCardは検証通りに動くけど、他のColdCardで実際その通りに動くかはTrustするかVerifyするかしかないかなと思います。CC以外のウォレットも、乱数がどういうものかを調べることは非常に重要です。
TrustとVerifyはトレードオフの関係にあります。失うものが大きすぎる場合は、時間的コスト、金銭的コストをかけてでもVerifyすべきです。Trustすると心の隅にモヤモヤが残ります。そして、Bitcoin並びに暗号資産において、失ったときが終わりの合図です。
一方で、VerifyせずにTrustするのも日常生活などにおいてある程度は大事です。大切な家族をTrustする、飲み水や食べ物をTrustする、机がいきなり壊れないことをTrustする。これは人や環境にもよりますが、すべての物事をVerifyしていてはコストが大きすぎて、他のリソースに力を割けません。何をTrustして何をVerifyするか、それを自分の中で考えていくことが重要です。
そして、Bitcoinにおいては「Don’t Trust, Verify」つまりVerifyが重要です。Verifyすると安心感が得られますし、さらにBitcoinの知識も増えます。知識が増えると、Bitcoinをより安全に安心して保管・利用できるようになり、一石二鳥です。シードフレーズのでき方を知らずにBTCを保有している人はたくさんいると思います。
僕はColdCardをこれまでしっかり検証して来なかったので、正直Trustしてたなと思います。でも、いきなりすべてVerifyするのは難しいので、気になることや不安が出たらVerifyしていこうと思います。
みなさんもぜひ、不安なことがあれば検証してみましょう!まずは、今使ってるウォレットの乱数生成がどうなっているかからですかね。
Not Your 乱数, Not Your Coins!
関連記事
「Subscribe」すると投稿の通知が届きます。ぜひご登録ください!
↓LNでのチップはこちら(1satoshiに感謝⚡)
moyashamo@getalby.com
X(Twitter):@sishamo_moyashi
過去の記事:
アーカイブからも閲覧できます。