NIP-04は危ない

注意: 筆者は暗号と数学の基本的な訓練を受けていないため根幹部分に嘘が含まれる可能性が有る。内容は全てウェブ上のランダムな情報源の受け売りである。

前書き

この記事はNostr Advent Calendar第二会場12月17日に参加している。 昨日はkotaroさんの「ライトな自己紹介記事」、明日はkgさんの「SNSの誹謗中傷はNostrで対策したらどうか」。

何を話したいか

NostrのDMやミュートリスト、ブックマークの暗号化などに既に広汎に使われているNIP-04がなぜ危険で有害なのか、NIP-44は何を解決するのか。

公開鍵暗号がなにをしているのかざっくりと

公開鍵暗号の基本がわからない人向け+自分用メモ。数学的な解説はjunさんの記事を。

Nostrで活躍しているのは公開鍵暗号を利用したデジタル署名である。Nostrで使われる鍵ペアで公開鍵暗号化をすることはない。公開鍵暗号と日本語で言うと暗号化アルゴリズム(cipher)を想起しがちだが、公開鍵暗号はpublic-key cryptographyであって暗号化アルゴリズムだけを指すわけではない。

RSAなら署名と暗号化は同じことだが、これはRSAが落とし戸付き一方向性関数という便利な性質を持っているからで、楕円曲線暗号で使われる離散対数問題にはまだ落とし戸が見つかっていないので暗号化と同じ方法で署名を行うことができない。RSAなら完全に署名と暗号化が可換かというと最近はそうでもなく、一応同じアルゴリズムだが復号/検証側に中華風余剰定理による最適化が行われているために公開鍵と秘密鍵で鍵の形式が異なったりしている。

RSA問題以外に基づく公開鍵暗号で普及しているものは離散対数問題の一方向性に基づいている。Elgamal署名、DSA、Schnorr署名、DH鍵共有、Elgamal暗号などである。Elgamal署名を改良したのがDSAで、NISTの標準曲線(secp256r1)と組み合わせてECDSAなどでよく使われている。DSAの前に発表されていたSchnorr署名のほうが署名のサイズや安全性で優れていた(Schnorr署名は離散対数問題が困難という仮定のもとで安全性を証明できる)が、特許の問題でSchnorr署名が使われるのは2008年まで待つ必要があった。

Schnorr署名を利用した署名アルゴリズムで有名なのがEdDSAで、ツイストエドワード曲線とSchnorr署名を組み合わせ、他の曲線が踏んでいるセキュリティ上の弱点を避けながら高速化をしており、ssh鍵などでデフォルトになっている。

Nostrで使われる鍵はsecp256k1で、SEC2が推奨する公開鍵暗号リストでNIST標準曲線などと共に紹介されたものである。ビットコインと同じ曲線だが、ビットコインと違って署名方式にSchnorr署名を使う。

先程Nostrの鍵ペアで公開鍵暗号化をすることは無いと書いたが、Nostrに限らず2023年現在公開鍵暗号をcipherとして使う事は殆どない。公開鍵暗号の最初期はRSAで暗号化した文字列を送るRSA鍵交換で鍵配送問題を解決したが、今はDH鍵共有やECDH鍵共有に替わられている。Elgamal暗号はDH鍵共有を暗号化方式として利用できるようにしたようなものだが、DH鍵共有で共有秘密を作れるならそれを共通鍵暗号の鍵として使うのに比べた優位性がない。

2014年のスノーデンの暴露以降は特に鍵が漏れても以前のメッセージが復号できない前方秘匿性が重視されるようになったため、ECDHの鍵を使い捨てで生成して鍵共有をする方式が普及した。ECDHの鍵を使い捨てにする場合鍵共有する相手が正しいかどうか確かめられないため、既知の静的な鍵は別に用意して署名によって相手を確かめることになる。

署名を利用して相手を確かめるためにチャレンジレスポンスが使われていると誤認させるような解説もあるが、チャレンジに対して署名することで相手を確かめることは基本できない。任意のチャレンジに対して署名 (めくら判を押す) するなら中間者攻撃が成立してしまうので、チャレンジを出す相手が正しいことが確かめられないといけない。これから相手が正しいか確かめるのに最初からチャレンジが正しいことの証明が必要になる。

例外的に公開鍵認証でチャレンジレスポンスをしているFIDO認証では予めTLSでチャレンジを送る側の正しさが検証できている前提で、そのサイトの認証にしか使えないことを明記した文章に署名を行う。 OpenSSHでは共有秘密から導出したセッションIDに対する署名、TLS1.3ではネゴシエーションでやり取りした全てのメッセージのハッシュに対する署名を行うことで中間者攻撃がなければ当事者以外署名の対象になる値を知り得ないようになっている。(中間者攻撃があれば値が変わる)

NostrのDM暗号化では静的な主鍵で鍵共有も行うため、前方秘匿性の確保はなされない。これを確保するためには相手クライアントが常に接続を待機して数秒でネゴシエーションを行う必要が有るためNostrやPGPのようなプロトコルでは非現実的である。

Nostrで使われる暗号化方法

NIP-04の暗号化手法

詳しくはnips-jaのnip-04を参考に。NIP-04は互いの鍵ペアのパラメータを使ってECDH鍵共有をして、共有秘密ををaesの鍵にしている。

共有秘密をハッシュ化せずにそのまま暗号化に用いているが、ECDH鍵共有をハッシュ化せずに使った場合、鍵の構造に対する興味深い攻撃方法 (Cheon’s attack) を助けることになる。この方式で大量にECDH鍵交換をすればNostrの主鍵を総当りする計算量を減らすことになる可能性が有る。

さらにAES-CBCの初期化ベクトルが予測不能であることが保証できないため、選択暗号文攻撃に対して脆弱になる可能性が有る。

現在のNIP-44候補の暗号化手法

  1. 暗号論的疑似乱数生成器を利用して32バイトのソルトを生成する。

  2. 相手と自分の鍵ペアでECDHを行って得られたハッシュ化していない共有秘密から32バイトを取得して会話鍵にする。 ソルトと会話鍵を合わせてSHA256を用いたHKDFに入れ、76バイトの出力を得る。 HKDFの出力は0-32バイトまでの暗号鍵、32-44バイトまでのChaCha nonce、44-76バイトまでのhmac keyに分ける。

  3. utf8からバイナリに変換してパディングをする

  4. ChaCha20を使って暗号化

  5. 暗号文からMACを計算する

この方法であればECDHの共有秘密を直接暗号鍵に使うのと比べて主鍵が危険にさらされることはなく、nonceも安全に導出される。 HMACで完全性を保証するので署名したあとに暗号化してプライバシーを向上させることもできる。

まとめ

最初の方の駄文が多くて実際に伝えたいことは10行ぐらいで終わるようなことになっているけどNIP-04で暗号化をするだけで秘密鍵まで危険に晒されることが伝わればいいかな。

クライアント開発に関わる人には頭が上がらないがNIP-04暗号化の実装を考えているならこれを見て思い留まってくれるとありがたい。NIP-44暗号化標準以外にもGift WrapやEncrypted GroupEventsとかいろいろDM周りの議論が活発だけど、暗号化周りは全て出揃ってfinalで勧告されるまではどのクライアントも実装せず使わないようにしておかないと暗号標準が乱立してNostrで安全なDMは二度とできなくなる。