ECDSA と Schnorr のどちらを使うかは、今日ではおもに Bitcoin の問題です。Taproot(BIP 340〜342、2021年11月に有効化)によって、同じ曲線 secp256k1 の上で ECDSA と並んで Schnorr 署名が使えるようになったためです。2 つの方式は同じ鍵を使い、同じ困難な問題に依拠していますが、Schnorr の線形な構造ではマルチシグ、しきい値署名、バッチ検証を自然に実現できるのに対し、ECDSA の構造ではそれらが扱いにくくなります。
比較表
| ECDSA(secp256k1) | Schnorr(BIP-340、secp256k1) | |
|---|---|---|
| 署名の式 | s = k⁻¹·(z + r·d) mod n | s = k + e·d mod n、e = H(R ‖ P ‖ m) |
| 公開鍵 | 圧縮形式で 33 バイト(非圧縮形式で 65) | 32 バイト、x-only(x 座標のみ) |
| 署名 | コンパクト形式で 64 バイト、Bitcoin のスクリプトでは DER で 70〜72 バイト | 64 バイト |
| 展性 | (r, n − s) も有効、Bitcoin は low-S を強制 | BIP-340 の検証ではなし |
| 公開鍵の復元 | 可能(Ethereum の ecrecover) | 不可 |
| 鍵集約 | マルチパーティ計算プロトコルが必要 | ネイティブ(MuSig2、BIP-327) |
| しきい値署名 | 複雑なプロトコル(GG18/20、CGGMP) | FROST(RFC 9591) |
| バッチ検証 | 不可 | 可能 |
| 安全性証明 | 一般群モデルのみ | ランダムオラクルモデル、離散対数問題 |
| 標準 | FIPS 186-5、SEC 1 | BIP-340。EdDSA(RFC 8032)は Schnorr の派生方式 |
| 採用例 | Bitcoin のレガシーと SegWit v0、Ethereum、大半の EVM チェーン | Bitcoin Taproot、Nostr、FROST を導入したシステム |
両者の違い
どちらも秘密の nonce k と、その点 R = k·G を使って署名します。ECDSA は、ハッシュ値 z、秘密鍵 d、r(R の x 座標)を、k のモジュラ逆数を介して組み合わせます。一方 Schnorr は、コミットメント、公開鍵、メッセージをハッシュしてチャレンジ e を作り、応答として s = k + e·d を返します。検証者はこれを s·G = R + e·P が成り立つかどうかで検証します。
この 1 つの設計上の選択から、ほかのすべての違いが生じます。
- 線形性:Schnorr 署名は足し合わせることができます。複数の署名者が nonce と部分応答を組み合わせて 1 つの署名にまとめることができ、その署名は全員の鍵の和に対して検証に通ります。MuSig2(BIP-327)はこれを n-of-n の署名として 2 ラウンドで行い、FROST(RFC 9591)は t-of-n のしきい値署名を行います。ECDSA では
kの逆数が線形性を壊すため、しきい値 ECDSA には重いマルチパーティプロトコルが必要です。こうしたプロトコルはウォレットやカストディ事業者に使われていますが、深刻なバグが見つかってきました。 - バッチ検証:多数の Schnorr 署名は、1 つずつ確認するよりも速くまとめて検証できます。これはブロックの検証で重要になります。ECDSA にはこのような近道がありません。
- 鍵へのコミットメント:
eは公開鍵も含めてハッシュするため、Schnorr 署名を別の鍵のもとで再利用することはできず、署名から鍵を復元することもできません。ECDSA では公開鍵の復元が可能で、Ethereum はバイト数を節約するためにこれに依存しています。Schnorr では、鍵を送信するか、あらかじめ知っている必要があります。 - 展性:ECDSA では
(r, s)と(r, n − s)のどちらも検証に通るため、Bitcoin は low-S と厳格な DER を強制しています。BIP-340 はエンコーディングを 1 つに固定しているため、第三者が有効な署名を改変することはできません。 - 歴史:Claus Schnorr が 1989年に発表した方式は特許で保護されており、DSA(と後の ECDSA)はその特許を避けるように設計されました。特許が期限切れになったのは 2008年で、ECDSA がすでに定着した後のことでした。EdDSA(2011年)は、エドワーズ曲線上で動作する決定的な Schnorr の派生方式です。
Taproot で実際に変わったこと
Taproot の出力は、32 バイトの x-only 鍵 1 つにコミットします。キーパスで使用するには 64 バイトの Schnorr 署名が 1 つあれば足ります。その鍵が 1 人のものでも、複数人の鍵を MuSig2 で集約したものでも同じなので、2-of-2 や 3-of-3 のマルチシグによる使用も、チェーン上では単一署名による通常の使用と見分けがつきません。代わりの使用条件はスクリプトのマークルツリーに置かれ、使われたときにだけ公開されます。単一鍵による使用を SegWit v0 で行う場合、ウィットネスには最大 72 バイトの DER 形式の ECDSA 署名、sighash バイト、33 バイトの公開鍵が含まれます。
次のスクリプトを Node.js 26.10 上の @noble/curves 2.3.0 で実行すると、同じ秘密鍵でのサイズの違いがわかります。
import { secp256k1, schnorr } from "@noble/curves/secp256k1.js";
import { sha256 } from "@noble/hashes/sha2.js";
import { utf8ToBytes } from "@noble/hashes/utils.js";
const secretKey = secp256k1.utils.randomSecretKey();
const msg = sha256(utf8ToBytes("pay 0.01 BTC to bc1p..."));
// ECDSA: 33-byte compressed public key, 64-byte compact (r, s) signature.
const ecdsaPub = secp256k1.getPublicKey(secretKey, true);
const ecdsaSig = secp256k1.sign(msg, secretKey, { prehash: false });
// BIP-340 Schnorr: 32-byte x-only public key, 64-byte (R.x, s) signature.
const schnorrPub = schnorr.getPublicKey(secretKey);
const schnorrSig = schnorr.sign(msg, secretKey);
console.log("ECDSA public key:", ecdsaPub.length, "bytes, signature:", ecdsaSig.length, "bytes, valid:", secp256k1.verify(ecdsaSig, msg, ecdsaPub, { prehash: false }));
console.log("Schnorr public key:", schnorrPub.length, "bytes, signature:", schnorrSig.length, "bytes, valid:", schnorr.verify(schnorrSig, msg, schnorrPub));
ECDSA public key: 33 bytes, signature: 64 bytes, valid: true
Schnorr public key: 32 bytes, signature: 64 bytes, valid: true
ECDSA のコンパクト形式も 64 バイトですが、Bitcoin のスクリプトは ECDSA 署名を DER で格納し、Ethereum は復元用のバイトを追加します。Schnorr の形式は、そのままの形でチェーンに載ります。
共通の落とし穴
どちらの方式も、不適切な nonce を許しません。2 つのメッセージで k を再利用したり、その数ビットを繰り返し漏らしたりすると、どちらの方式でも秘密鍵が明らかになります。BIP-340 は、鍵とメッセージに 32 バイトの新しい補助乱数を混ぜて nonce を導出することを推奨しています。マルチパーティの Schnorr には新しいルールが加わります。MuSig2 の nonce は、決してメッセージだけから決定的に導出してはいけません。ほかの参加者を支配する攻撃者が、同じ nonce で 2 つの異なる集約チャレンジに署名させることができるためです。プリミティブを自分で組み合わせるのではなく、libsecp256k1 の MuSig2 モジュールのような監査済みの実装を使ってください。
どちらを選ぶべきか
- 新しい Bitcoin ウォレットと出力:Schnorr 署名による Taproot のキーパス使用を選びます。参加者が連携できる複数人の鍵には MuSig2 を、フォールバックにはスクリプトパスを使います。
- Bitcoin のメッセージ署名:Taproot アドレスには BIP-322 を使います。旧来の BIP-137 形式は ECDSA しか扱えないためです。
- Ethereum と EVM チェーン:ECDSA を使います。トランザクション、
ecrecover、ウォレットが ECDSA を前提としているためです。スマートアカウントなら、コントラクトのコードでほかの方式を検証できます。 - Nostr:NIP-01 の要件どおり、BIP-340 Schnorr を使います。
- しきい値署名によるカストディ:検証者が Schnorr 署名または EdDSA 署名を受け入れる場合は FROST を使います。しきい値 ECDSA は、対象のチェーンが ECDSA を要求する場合に限ります。
- ブロックチェーン以外のシステム:Ed25519 なら、標準(RFC 8032)、ライブラリのサポート、決定的な nonce を備えた Schnorr の設計を利用できます。
移行時の注意点
Bitcoin では同じ秘密鍵で両方の種類の署名を作れますが、何が必要かを決めるのは出力です。P2WPKH や P2PKH の出力から P2TR の出力へ資金を移すことが、ここでの移行にあたります。
- Taproot(BIP-86 の導出パス
m/86'/0'/0')と PSBT の Taproot フィールドに対応したバージョンに、ウォレットのソフトウェアとハードウェアをアップグレードします。 - x-only 鍵を正しく使います:BIP-340 の鍵はパリティバイトを持たず、鍵はスクリプトツリーのコミットメントで tweak されます。ECDSA の公開鍵のエンコーディングを流用しないでください。
- Taproot アドレスで所有権を証明できるよう、メッセージ署名を BIP-322 に切り替えます。
- マルチシグでは、MuSig2 によるキーパスでの集約(プライバシーが最も高く、最も安価。ただし対話が必要)と、Tapscript の
OP_CHECKSIGADDを使うスクリプトパス(対話は不要。ただし使用時にポリシーが公開される)のどちらにするかを決めます。 - 古い出力のために ECDSA の検証は続けます。コンセンサスルールは両方を無期限にサポートします。