本文へスキップ
ecdsa.com
Rust

ring

ring は Brian Smith による Rust の暗号ライブラリで、rustls や多くの Web サーバーの基盤です。API は意図的に小さく、アルゴリズム定数で署名のエンコーディングが決まり、秘密鍵は PKCS#8 のみ、曲線は P-256、P-384、Ed25519 です。
ライセンス
Apache-2.0 AND ISC
確認したバージョン
0.17.14 ·
ソースコード
github.com/briansmith/ring
ドキュメント
docs.rs/ring/0.17.14/ring/

互換性の概要

ECDSAEdDSA

ECDSA の曲線
P-256P-384
EdDSA
Ed25519
公開鍵の形式
sec1-uncompressed

ring が提供するアルゴリズムは少数に絞られており、BoringSSL 由来のアセンブリと誤用しにくい API を備えています。nonce を選ぶ方法も、事前に計算したダイジェストに署名する方法も、珍しい形式の鍵を読み込む方法もありません。そのため高速で使い方を誤りにくい一方、作業の大半は他のスタックからの鍵や署名の変換になります。

エンコーディングはアルゴリズムの一部です。ECDSA_P256_SHA256_FIXED_SIGNING は 64 バイトの生の r‖s を、ECDSA_P256_SHA256_ASN1_SIGNING は DER を生成し、検証用にもそれぞれ対応する _FIXED と _ASN1 の定数があります。もう一方の定数で検証した署名は単に Unspecified で失敗するため、JWS の署名を ASN1 定数で検証したり、OpenSSL の署名を FIXED で検証したりすると、偽造された署名とまったく同じに見えます。サンプルでは両方の結果を示しています。署名にはランダムな nonce が使われ、s は正規化されず、検証は low-S と high-S のどちらも受け入れます。

秘密鍵は PKCS#8 DER としてしか読み込めず、Ed25519 鍵は公開鍵を含む PKCS#8 v2 でなければなりません。OpenSSL やほとんどのツールが書き出す 48 バイトの v1 形式は、Ed25519KeyPair::from_pkcs8 では VersionNotSupported で拒否されましたが、from_pkcs8_maybe_unchecked と from_seed_unchecked では受け入れられました。PEM、SEC1、JWK の秘密鍵は、JWK 変換ツールなどで先に変換する必要があります。公開鍵は SEC1 の点そのものです。public_key() は 65 バイトの非圧縮形式を返し、圧縮形式の点は受け付けられません。

対応する曲線は P-256 と P-384、それに Ed25519 だけです。P-521、secp256k1、ポスト量子アルゴリズムはありません。これらや FIPS 認証が必要なプロジェクトは、ring 互換の API を提供する aws-lc-rs に移行します。

ring の署名がどのエンコーディングかを確認するには、DER ⇄ 生形式の変換ツールに貼り付けてください。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/ring/run.sh がコンテナイメージ rust:1.99.1-trixie 内で ring 0.17.14 を実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • ECDSA 署名のエンコーディング

    der-or-raw

    アルゴリズム定数がエンコーディングを決めます。*_FIXED は 64 バイトの生の r‖s を、*_ASN1 は DER を署名・検証し、もう一方の定数では署名は検証を通りません。

    チェックによる記録
    fact ecdsa-signature-encoding = der-or-raw
    evidence ecdsa-signature-encoding: FIXED signs 64 bytes; ASN1 output starts with 0x30 (a DER SEQUENCE)
    evidence ecdsa-signature-encoding: a signature verifies under the other encoding's constant: false
  • ECDSA の nonce

    random

    nonce はランダムです。同じメッセージの 2 つの署名は異なり、決定的 nonce のオプションはありません。

    チェックによる記録
    fact ecdsa-nonce = random
  • 署名時の low-S

    either

    s は正規化されません。64 個の署名の中に high-S の値が現れました。

    チェックによる記録
    fact ecdsa-low-s-sign = either
  • 検証時の high-S

    accepts-high-s

    FIXED 定数による検証は、s を n − s に置き換えた署名を受け入れました。

    チェックによる記録
    fact ecdsa-low-s-verify = accepts-high-s
  • 署名呼び出しがハッシュする対象

    hashes-message

    メッセージはアルゴリズムが定めるハッシュでハッシュされます。事前に計算したダイジェストに署名する API はありません。

    チェックによる記録
    fact ecdsa-prehash = hashes-message
    evidence ecdsa-prehash: the API has no entry point for signing a precomputed digest
  • 不正な形式の ECDSA 署名

    returns-error

    verify() は、形式の誤った署名にも正しくない署名にも、詳細なしで Err(Unspecified) を返します。

    チェックによる記録
    fact ecdsa-malformed-signature = returns-error
    evidence ecdsa-malformed-signature: verify() of 3 bytes: Err(Unspecified)
  • ECDSA の曲線

    P-256P-384

    P-256 と P-384 のみです。

    チェックによる記録
    fact ecdsa-curves = P-256, P-384
    evidence ecdsa-curves: ring defines ECDSA_P256_* and ECDSA_P384_* algorithms only
  • EdDSA

    Ed25519

    Ed25519 です。

    チェックによる記録
    fact eddsa = Ed25519
  • Ed25519 の PKCS #8 v1 鍵

    rejected-by-from-pkcs8

    48 バイトの PKCS#8 v1 形式の Ed25519 鍵は、from_pkcs8 では VersionNotSupported で拒否されましたが、from_pkcs8_maybe_unchecked と from_seed_unchecked では受け入れられました。generate_pkcs8 は 83 バイトの v2 鍵を書き出します。

    チェックによる記録
    fact ed25519-pkcs8-v1 = rejected-by-from-pkcs8
    evidence ed25519-pkcs8-v1: generate_pkcs8 writes 83 bytes (PKCS#8 v2 with the public key); a 48-byte v1 key: from_pkcs8 Err(KeyRejected("VersionNotSupported")), from_pkcs8_maybe_unchecked ok true, from_seed_unchecked ok true
  • ポスト量子アルゴリズム

    none

    ポスト量子アルゴリズムには対応していません。

    チェックによる記録
    fact pqc = none
  • 公開鍵の形式

    sec1-uncompressed

    公開鍵は SEC1 の非圧縮形式の点(P-256 では 65 バイト)です。圧縮形式の点は失敗し、SPKI、PEM、JWK を解析する機能はありません。

    チェックによる記録
    fact public-key-formats = sec1-uncompressed
    evidence public-key-formats: public_key() returns 65 bytes (uncompressed SEC1); no SPKI, PEM or JWK parsing
  • 秘密鍵の形式

    pkcs8-der

    秘密鍵は PKCS#8 DER からしか読み込めません。

    チェックによる記録
    fact private-key-formats = pkcs8-der

使い方

FIXED(生形式)の定数で署名し、対応する検証用の定数だけがその署名を受け入れることを示します。
use ring::rand::SystemRandom;
use ring::signature::{self, EcdsaKeyPair, KeyPair, UnparsedPublicKey};

fn main() {
    let rng = SystemRandom::new();
    // ring only reads PKCS#8 DER private keys.
    let pkcs8 = EcdsaKeyPair::generate_pkcs8(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, &rng).unwrap();
    let message = b"order #1042 approved";

    // The algorithm constant fixes the encoding: FIXED = raw r||s (JWS, WebCrypto),
    // ASN1 = DER (OpenSSL, Java). Verify with the matching constant.
    let fixed = EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, pkcs8.as_ref(), &rng).unwrap();
    let raw = fixed.sign(&rng, message).unwrap();
    let public = fixed.public_key().as_ref(); // 65-byte uncompressed SEC1 point

    let ok = UnparsedPublicKey::new(&signature::ECDSA_P256_SHA256_FIXED, public).verify(message, raw.as_ref()).is_ok();
    let wrong = UnparsedPublicKey::new(&signature::ECDSA_P256_SHA256_ASN1, public).verify(message, raw.as_ref()).is_ok();
    println!("raw bytes: {}, verified: {ok}, verified as DER: {wrong}", raw.as_ref().len());
}

スニペットのテストハーネスは、すべてのサンプルをコンテナイメージ rust:1.99.1-trixie 内でそのまま実行し、出力が一致しなければ実行は失敗します。

コマンド
cargo run --quiet --release
一致すべき出力(正規表現)
raw bytes: 64, verified: true, verified as DER: false

ライブラリ