本文へスキップ
ecdsa.com

決定的 ECDSA とランダム化 ECDSA の比較|RFC 6979、ランダムな nonce とヘッジ付き nonce

ECDSA の署名には毎回、秘密で、一意で、偏りのない nonce が必要です。それを生成する 3 つの方法は破綻のしかたがそれぞれ異なり、検証者にはどの方法で生成されたかを区別できません。

決定的 ECDSA とランダム化 ECDSA の違いは、ただ 1 つの値、つまり署名ごとの nonce k に関するものです。この値の品質が、秘密鍵が秘密のままでいられるかどうかを決めます。nonce が 1 回重複するだけでも、多数の署名にわたってわずかな偏りがあるだけでも、鍵を計算するには十分だからです。FIPS 186-5 はこの値を生成する方法を 2 つ認めており、3 つ目の方法であるヘッジ付き(hedged)署名はその 2 つを組み合わせたものです。

比較表

ランダム化決定的(RFC 6979)ヘッジ付き
nonce の生成方法署名ごとに新しい CSPRNG 出力秘密鍵とメッセージハッシュに対する HMAC-DRBG決定的な導出に新しい乱数を混合
標準FIPS 186-5 §6.3.1、付録 A.3.1 と A.3.2RFC 6979 §3.2、FIPS 186-5 §6.3.2 と付録 A.3.3RFC 6979 §3.6 のオプション、期限切れの CFRG ドラフト、BIP-340 の補助乱数
同じメッセージに 2 回署名異なる署名同一の署名異なる署名
乱数生成器が弱い、または壊れている鍵が漏洩しうる影響なし影響なし
繰り返し署名時のフォールト注入悪用は困難鍵が漏洩しうる悪用は困難
再現可能なテストベクトルなしあり(RFC 6979 付録 A.2)追加入力を固定した場合のみ
検証者側の変更不要不要不要
デフォルトで採用OpenSSL、Node.js、Java、Python cryptographylibsecp256k1、@noble/curvesオプトイン:@noble/curves の extraEntropy: true、BIP-340 の署名

それぞれの破綻のしかた

ランダム化 nonce は、乱数生成器に完全に依存します。生成器が同じ値を繰り返すと(Sony の PlayStation 3 は定数を使っていました)、鍵は簡単な代数計算で求められてしまいます。偏りがあるだけでも、たとえば上位ビットが常にゼロである場合や、タイミングのサイドチャネルから nonce の長さが漏れる場合には、隠れ数問題を解く格子の手法によって、数百〜数千の署名から鍵が復元されます。Minerva と TPM-Fail(2019年)は、まさにこの方法でスマートカードと TPM から鍵を復元しました。起動直後に署名する組み込みデバイス、クローンされた仮想マシン、フォークされたプロセスが、典型的なリスク環境です。

決定的 nonce では、乱数生成器が関与しなくなります。k は秘密鍵とメッセージハッシュから HMAC で導出されるため、同じ入力からは常に同じ nonce が得られ、異なるメッセージからは互いに無関係な nonce が得られます。リスクは物理的な攻撃に移ります。デバイスが同じメッセージに 2 回署名している間に攻撃者がグリッチを起こせると、同じ nonce による正しい署名と誤った署名が 1 つずつ得られ、そこから鍵が明らかになる可能性があります。Poddebniak らは 2018年に、決定的 ECDSA と EdDSA に対するこうしたフォールト攻撃を実証しました。決定的署名では署名が再現可能にもなります。これを必要とするプロトコルもあれば、その情報を漏らしてはならないプロトコルもあります。

ヘッジ付き nonce では、決定的な入力と新しい乱数の両方を導出に投入します。乱数生成器が壊れると方式は決定的署名に退化し、フォールト攻撃は毎回異なる nonce に直面します。RFC 6979 §3.6 はすでに追加入力を認めており、BIP-340 は 32 バイトの補助乱数を推奨し、ML-DSA(FIPS 204 §3.4)はデフォルトでヘッジ付きです。ヘッジ付きの ECDSA と EdDSA を標準化する IRTF のドラフトは、RFC にならないまま 2025年に期限切れになりました。

3 つとも、生成されるのはごく普通の署名です。検証者はこれらを区別できません。つまり、同一のメッセージから同一の署名が生成されることに気づく以外に、署名者の nonce の生成戦略を出力から監査する方法はありません。

各ライブラリの動作

  • OpenSSL は、デフォルトではランダムな nonce で署名します。OpenSSL 3.2(2023年11月)では、署名パラメーター nonce-type を 1 に設定することで RFC 6979 を使えるようになりました。この場合はダイジェストを明示的に指定する必要があります。OpenSSL 3.6 では、FIPS プロバイダーでも決定的 ECDSA を使えるようになりました。コマンドラインでは openssl pkeyutl -sign -pkeyopt nonce-type:1 -pkeyopt digest:sha256 です。
  • Python cryptography はデフォルトでランダム化されており、バージョン 43.0(2024年7月)以降は ec.ECDSA(hashes.SHA256(), deterministic_signing=True) を提供しています。
  • Node.js crypto.sign() と WebCrypto はランダムな nonce を使い、それを変更するオプションはありません。
  • @noble/curves は、デフォルトで RFC 6979 により k を導出し、extraEntropy: true または追加のバイト列を指定して呼び出すとヘッジ付きになります。
  • Bitcoin Core が使う libsecp256k1 は、任意の追加データを伴う RFC 6979 を使います。
  • EdDSA は定義上決定的です(RFC 8032)が、それでも乱数を加えるプラットフォーム実装もあります。

この違いは、cryptography 46.0.3 を使った数行の Python で確認できます。

Python:1 つの P-256 鍵によるランダム化 ECDSA と決定的 ECDSA
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec

key = ec.generate_private_key(ec.SECP256R1())
message = b"invoice 2026-10-20 #4411"

randomized = [key.sign(message, ec.ECDSA(hashes.SHA256())) for _ in range(2)]
deterministic = [
    key.sign(message, ec.ECDSA(hashes.SHA256(), deterministic_signing=True)) for _ in range(2)
]

print("randomized signatures equal:   ", randomized[0] == randomized[1])
print("deterministic signatures equal:", deterministic[0] == deterministic[1])
for sig in randomized + deterministic:
    key.public_key().verify(sig, message, ec.ECDSA(hashes.SHA256()))
print("all four verify with the same verifier")
出力
randomized signatures equal:    False
deterministic signatures equal: True
all four verify with the same verifier

テストと再現性

決定的署名なら、既知解テストが可能になります。RFC 6979 の付録 A.2 には P-192〜P-521 やその他の曲線について期待される署名が掲載されており、テストベクトルツールや Project Wycheproof はこうしたベクトルを基にしています。ランダム化署名やヘッジ付き署名では、署名が検証に通ること、そして不正な形式の署名が通らないことしかテストできません。監査では、疑わしい署名者を確認する方法としても決定的モードが使えます。同じメッセージに 2 回署名し、出力が繰り返されるかどうかを確認します。

手元にある署名で実際の nonce の再利用を検出するには、1 つの鍵による署名どうしで r の値を比較します。r が重複していれば nonce が重複しています。nonce 再利用チェッカーは、この比較を署名の束に対してまとめて行います。

どれを選ぶべきか

  • 正常な OS の乱数生成器を持つサーバーと CI:3 つのどれでも問題ありません。依存関係を 1 つなくすため、決定的署名またはヘッジ付き署名を優先します。
  • 組み込みデバイス、起動初期のコード、クローンされた VM:決定的署名またはヘッジ付き署名を使い、純粋にランダムな nonce は決して使いません。
  • 攻撃者にさらされるハードウェア(ハードウェアウォレット、スマートカード、決済端末):ヘッジ付き署名を使い、各署名を出力する前に検証するなどのフォールト対策と組み合わせます。
  • テストスイートと相互運用性の検証作業:ベクトルを再現できるよう、決定的署名を使います。
  • 同一の署名が繰り返されると情報が漏れるプロトコル(たとえば、同じメッセージに再び署名したという事実):ランダム化署名またはヘッジ付き署名を使います。

移行時の注意点

nonce の生成戦略を切り替えて変わるのは、署名側だけです。鍵、署名形式、検証者はそのままです。

  1. ライブラリの対応状況を確認する:あわせてバージョンも確認します。OpenSSL 3.2 以降、cryptography 43 以降、または RFC 6979 を内蔵したライブラリが必要です。
  2. RFC 6979 を有効にするときは、ダイジェストが明示的に設定されていることを確認します。導出がダイジェストに依存するためです。
  3. 1 つのメッセージに 2 回署名し、期待どおりの動作(決定的なら同一、ヘッジ付きなら異なる)を確かめる回帰テストを追加します。
  4. それでも乱数の健全性を保つ:ヘッジ付き署名でも良質な乱数生成器があるほうが有利で、鍵の生成には常に乱数生成器が必要です。
  5. 決定的署名によって初めて「同じ入力なら同じ出力」が成り立つようになるため、署名を比較またはキャッシュしている箇所をすべて見直します。

参考資料

すべての比較