bech32 の 32 は
10 + 26 - 4 = 32
数字 + アルファベット - 紛らわしいアルファベット = 32
なので大文字は NG (32じゃなくなってしまう)。表記の際にデバイスの都合で大文字で書いてもいいけど encode / decode の計算のときは小文字必須。と理解してます。
@scure の既定値を変えてもらう話はビットコインの実装の話なのであまり筋がよくないと思いますが、NIP-06 で明記するよう求めるのは良いかもしれません。
主張点は先程の BIP-32 で 256 が advised されている件と、Snort で PR が通っているというのが使えると思います。
自分の疑問は、「128 bit のシードを使ったとしてもマスターキーから derive すれば 256 bit と同程度に安全になる」という主張があるのかどうか、です。(例えば derive が重い処理なのでそう簡単に計算出来るわけじゃない、とか)
あ、勘違いしていた。BIP-39 を修正する話じゃなくて @scure の実装の既定値の話ですね。多分できると思いますが、今まで 128 bit 前提で実装してしているアプリがどれくらい困るかによると思います。
12 word 前提で UI を作ってたら急に 24 word になって UI がおかしくなるとかはありそうです。
自分もその理解です。 128 bit 総当りすれば解けちゃう鍵が生成されているという認識です。
BIP-32 で 256 bits が勧告されているのはここです。
> Generate a seed byte sequence S of a chosen length (between 128 and 512 bits; 256 bits is advised) from a (P)RNG.
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#master-key-generation
とりあえず、後で scrapbox にまとめておきます。
取り急ぎ、bip39 では既定値については何も書いてないですが、bip32 では 256 bit が勧告 (advised) されています。なので bip39 の既定値は 256 bit であるべきという論拠はありそうです。
自分の理解では、bip32 の場合は、マスターシード(=ニーモニック)から階層的に鍵を導出していくのでシードの強度にそれほど敏感になる必要はないのかもしれませんが、NIP-06 はマスターシードからNostr 秘密鍵を決定論的に導出しているので、マスターシードの強度がそのままに秘密鍵に反映してしまうのではと考えています。
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#master-key-generation
また nostr-tools か...
blockcore も 128 bit のへぼい鍵を生成してた。
snort が 128 bit エントロピーの鍵を使ってたので PR 出したんだけど、 nostr-tools の実装が 128 bit なのが元凶の気がしてきた。
確定申告を郵送する時に、局員の人がやたらレターパックを推してくるので怪しんでしまった。レターパック=詐欺のイメージが刷り込まれ過ぎている...
Ingress 懐かしい。自分もレジスタンスでした。2014 年頃か。過去に訪れたポータルを Google Maps で保存する Web アプリを作ってました。
NIP-06 とは関係なしに、BIP-39 に従って Nostr 秘密鍵と1対1に対応するニーモニックを作成するスクリプト。
https://github.com/susumuota/nostr-keyx/blob/main/bin/npub2hex.ts
NIP-06 は、最初にエントロピー(=ニーモニック)を生成してから Nostr 秘密鍵を**導出**する構造になっているので、既に秘密鍵を持っている人は、その秘密鍵を NIP-06のニーモニックに変換することは出来ない。
が、NIP-06とは関係なしに、BIP-39 に従って Nostr 秘密鍵と1対1に対応するニーモニックは作成可能で、これを紙に書き写すという手はあります。(NIP-06 と混同しないように注意)
Snort の markdown がひどい...
NIP-06 の実装方法 (*0)
- BIP-39 にてニーモニック(= entropy) を生成
- 256 bit (32 byte)の疑似乱数を生成 (entropy と呼ぶ)
- entropy からニーモニックに変換
- entropy とニーモニックは1対1に相互に変換可能(Reversible, base2048 相当なので)
- BIP-32 にて entropy から Nostr 秘密鍵を導出
- entropy をシードとしてマスターキーを生成 (*1) (*2)
- マスターキーから Nostr 用の秘密鍵を導出 (*3) (*4)
- ここで導出しているので Nostr 秘密鍵から元の entropy = ニーモニックに変換することは出来ない
- (*0) https://github.com/nostr-protocol/nips/blob/master/06.md
- (*1) https://github.com/paulmillr/scure-bip32/blob/2274f2f42c5382dd08aab747ba7c8686322e706d/index.ts#L89
- (*2) https://bips.xyz/32#master-key-generation
- (*3) https://github.com/paulmillr/scure-bip32/blob/2274f2f42c5382dd08aab747ba7c8686322e706d/index.ts#L174
- (*4) https://bips.xyz/32#child-key-derivation-ckd-functions
- 実装例
- https://github.com/susumuota/nostr-keyx/blob/main/bin/genkey.ts
- https://github.com/v0l/snort/pull/425/files
https://scrapbox.io/nostr/s_ota にメモを書いているのでまとまったら NIP-06 に写しておきます。
ニーモニックの計算方法(BIP-39)
- strength 256 bitの場合
- 256 bit (32 byte)の疑似乱数を生成 (entropy と呼ぶ) (*1)
- entropy のチェックサムを計算(SHA256 の 0 バイト目, 8 bit) (*2)
- それをくっつけて 256 + 8 = 264 bit (33バイト)の整数を生成
- 264 bit の整数は
2^264 = 2^(11*24) = (2^11)^24 = 2048^24
- となり、2048個の単語リストから1単語ずつ選んで24個並べると表現出来る
base64 を知ってる人は base2048 と考えれば簡単。
- base64: データを 6 bit ずつ分割し、それぞれ 64 個 (2^6個) の文字のうち一つを割り当てていく
- base2048: データを 11 bit ずつ分割し、それぞれ 2048 個 (2^11個) の単語のうち一つを割り当てていく
秘密鍵は32バイトなら何でもいいわけじゃなくて、公開鍵の生成の時に、
Fail if d' = 0 or d' ≥ n.
という条件があるので 0000... や FFFF.... とかは公開鍵生成の時にNGになるはず。
https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#public-key-generation
一応結果はこんな感じ。
秘密鍵から公開鍵の計算に29秒
32バイトのメッセージの署名に158秒
検証に86秒。
```
Test vector #0:
pubkey_gen took: 29071585
schnorr_sign took: 158253199
* Passed signing test.
schnorr_verify took: 85828791
* Passed verification test.
```
Raspberry Pi Pico + MicroPython + secp256k1.schnorr.sign は動くけど遅すぎ。次は Raspberry Pi Zero W やってみる。
ここキャッシュしたけど 0.01 % くらいしか速くならなかった...
Raspberry Pi Pico の MicroPython で secp256k1.schnorr.sign を計算させてみた。32バイトのメッセージの署名に3分くらいかかる。テストは全部通った。途中の計算をキャッシュ出来そうなのでもう少し速く計算できそうなのでやってみる。