RustCrypto プロジェクトは ECDSA を、汎用の ecdsa クレートと曲線ごとのクレート(p256、p384、p521、secp256k1 用の k256、Brainpool のクレート群)に分けており、いずれも signature トレイト(Signer、Verifier、PrehashSigner)を実装しています。C の依存関係を持たない純粋な Rust 実装のため、WebAssembly や組み込み向けのターゲットでは定番の選択肢です。チェックでは ecdsa 0.17.0 と、p256、p384、k256 の 0.14.0 を使いました。
署名は決定的です。RFC 6979 のテストメッセージに対する P-256 署名は、RFC のテストベクトルとバイト単位で一致しました。Signature 型は 64 バイトの生の r‖s 形式で、DER は明示的な to_der() と from_der() で扱います。誤った形式を解析すると、検証が行われる前にエラーが返ります。Signer トレイトは曲線のデフォルトのハッシュ(P-256 では SHA-256)でメッセージをハッシュします。PrehashSigner::sign_prehash はダイジェストを受け取り、メッセージの SHA-256 に対して同一の署名を生成しました。
low-S の扱いは曲線によって異なり、1 つのコードベースで両方の種類の曲線を扱うときの落とし穴になります。NIST 曲線のクレートは s を計算されたまま残し(P-256 の署名 64 個のうち 29 個が high-S でした)、その検証処理は low-S と high-S のどちらも受け入れます。k256 は署名時に s を正規化し、その検証処理は有効な署名を反転させた high-S 形式を拒否しました。これは Bitcoin のルールと一致します。そのため OpenSSL、Java、クラウドの KMS が生成した secp256k1 署名は、k256 では約半分の確率で失敗します。サンプルが P-256 で示しているように、先に normalize_s() を呼び出してください。
鍵は SPKI と PKCS#8(pem と pkcs8 のフィーチャーを有効にすれば PEM と DER)、圧縮・非圧縮いずれの形式の SEC1 の点、SEC1 の秘密鍵、生のスカラーから読み込めます。Ed25519 は別の ed25519-dalek クレートにあり、ポスト量子アルゴリズムはありません。Rust で FIPS 認証済みのプリミティブやポスト量子のプリミティブが必要な場合は aws-lc-rs を参照してください。
署名が決定的なので、テストで署名が変わったら、それは本物のリグレッションの兆候です。他のライブラリとの比較にはテストベクトルと DER ⇄ 生形式の変換ツールを使ってください。