決定的 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.2 | RFC 6979 §3.2、FIPS 186-5 §6.3.2 と付録 A.3.3 | RFC 6979 §3.6 のオプション、期限切れの CFRG ドラフト、BIP-340 の補助乱数 |
| 同じメッセージに 2 回署名 | 異なる署名 | 同一の署名 | 異なる署名 |
| 乱数生成器が弱い、または壊れている | 鍵が漏洩しうる | 影響なし | 影響なし |
| 繰り返し署名時のフォールト注入 | 悪用は困難 | 鍵が漏洩しうる | 悪用は困難 |
| 再現可能なテストベクトル | なし | あり(RFC 6979 付録 A.2) | 追加入力を固定した場合のみ |
| 検証者側の変更 | 不要 | 不要 | 不要 |
| デフォルトで採用 | OpenSSL、Node.js、Java、Python cryptography | libsecp256k1、@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 で確認できます。
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 の生成戦略を切り替えて変わるのは、署名側だけです。鍵、署名形式、検証者はそのままです。
- ライブラリの対応状況を確認する:あわせてバージョンも確認します。OpenSSL 3.2 以降、
cryptography43 以降、または RFC 6979 を内蔵したライブラリが必要です。 - RFC 6979 を有効にするときは、ダイジェストが明示的に設定されていることを確認します。導出がダイジェストに依存するためです。
- 1 つのメッセージに 2 回署名し、期待どおりの動作(決定的なら同一、ヘッジ付きなら異なる)を確かめる回帰テストを追加します。
- それでも乱数の健全性を保つ:ヘッジ付き署名でも良質な乱数生成器があるほうが有利で、鍵の生成には常に乱数生成器が必要です。
- 決定的署名によって初めて「同じ入力なら同じ出力」が成り立つようになるため、署名を比較またはキャッシュしている箇所をすべて見直します。