Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 16390] ファイルの概要

このコミットは、Go言語の標準ライブラリ crypto/rsa パッケージにRSASSA-PSS (Probabilistic Signature Scheme) 署名アルゴリズムの実装を追加するものです。具体的には、以下のファイルが変更されています。

  • src/pkg/crypto/rsa/pss.go: RSASSA-PSSのエンコーディング (emsaPSSEncode) および検証 (emsaPSSVerify) ロジック、そして署名 (SignPSS) および検証 (VerifyPSS) の高レベルAPIが新規追加されました。
  • src/pkg/crypto/rsa/pss_test.go: pss.go で実装されたRSASSA-PSS機能の単体テストが新規追加されました。これには、PKCS#1 v2.1のテストベクトル (pss-vect.txt.bz2) を用いたゴールデンテストや、OpenSSLとの互換性テスト、様々なソルト長での署名・検証テストが含まれます。
  • src/pkg/crypto/rsa/rsa_test.go: 既存のRSAテストファイルに、big.Int の文字列変換に関する小さな修正が加えられています。これはPSSの実装とは直接関係ありませんが、テストユーティリティの一部として変更されています。
  • src/pkg/crypto/rsa/testdata/pss-vect.txt.bz2: RSASSA-PSSのテストベクトルを含むバイナリファイルが追加されました。これは pss_test.go で利用されます。

コミット

commit 876455f3ba0e3ee66e177cf901ff5ea9c5aa9f07
Author: Nan Deng <monnand@gmail.com>
Date:   Thu May 23 11:10:41 2013 -0400

    crypto/rsa: implement PSS signatures.
    
    This change contains an implementation of the RSASSA-PSS signature
    algorithm described in RFC 3447.
    
    R=agl, agl
    CC=gobot, golang-dev, r
    https://golang.org/cl/9438043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/876455f3ba0e3ee66e177cf901ff5ea9c5aa9f07

元コミット内容

crypto/rsa: implement PSS signatures.

This change contains an implementation of the RSASSA-PSS signature
algorithm described in RFC 3447.

R=agl, agl
CC=gobot, golang-dev, r
https://golang.org/cl/9438043

変更の背景

このコミットの主な背景は、Go言語の crypto/rsa パッケージに、よりセキュアで推奨されるRSA署名スキームであるRSASSA-PSSを追加することです。

従来のRSA署名スキームであるRSASSA-PKCS1-v1_5(PKCS#1 v1.5パディング)は、長年にわたり広く利用されてきましたが、その構造に起因するいくつかの脆弱性や攻撃手法が指摘されていました。特に、特定の条件下での選択的偽造攻撃(e.g., Bleichenbacher's attack on PKCS#1 v1.5 signatures)のリスクが懸念されていました。

RSASSA-PSSは、PKCS#1 v2.1(RFC 3447で定義)で導入された新しい署名スキームであり、以下の点でPKCS#1 v1.5よりも優れています。

  1. 確率的性質 (Probabilistic Nature): PSSは、署名生成プロセスにランダムなソルト(salt)値を導入します。これにより、同じメッセージと秘密鍵から生成される署名が毎回異なるものとなり、決定論的な署名スキームに存在する可能性のある攻撃(例えば、署名オラクル攻撃)に対する耐性が向上します。
  2. より厳密なセキュリティ証明 (Stronger Security Proofs): PSSは、ランダムオラクルモデル(Random Oracle Model, ROM)において、RSA問題の困難性に基づいたより厳密なセキュリティ証明が与えられています。これは、特定の仮定の下で、PSSが既存の攻撃に対して安全であることを数学的に保証するものです。
  3. パディングの堅牢性 (Robust Padding): PSSのパディングスキームは、PKCS#1 v1.5のそれよりも複雑で、メッセージのハッシュ値、ソルト、および特定の定数からマスク生成関数(MGF1)を用いてマスクを生成し、データブロックに適用します。これにより、パディングの構造を悪用した攻撃が困難になります。

これらの理由から、現代の暗号システムではRSASSA-PSSの使用が強く推奨されています。Go言語の標準ライブラリにこの機能を追加することで、開発者はよりセキュアな署名メカニズムを容易に利用できるようになり、Goアプリケーションの全体的なセキュリティレベルが向上します。

前提知識の解説

このコミットの理解には、以下の暗号技術に関する前提知識が必要です。

1. RSA暗号システム

RSAは、公開鍵暗号方式の一つで、データの暗号化、デジタル署名、鍵交換などに広く用いられます。

  • 公開鍵 (Public Key): (n, e) のペアで構成されます。n は2つの大きな素数 pq の積 (n = p * q)、e は公開指数です。
  • 秘密鍵 (Private Key): (n, d) のペアで構成されます。d は秘密指数で、e * d ≡ 1 (mod φ(n)) を満たします(φ(n) はオイラーのトーシェント関数)。
  • 暗号化: 平文 MC = M^e mod n で暗号化します。
  • 復号化: 暗号文 CM = C^d mod n で復号化します。
  • デジタル署名: メッセージ M のハッシュ値 H(M) を秘密鍵で署名します (S = H(M)^d mod n)。検証者は公開鍵を使って H(M) = S^e mod n を計算し、元のハッシュ値と比較します。

2. デジタル署名

デジタル署名は、メッセージの完全性(改ざんされていないこと)と送信者の認証(誰が送ったか)を保証する技術です。

  • 署名生成: 送信者はメッセージのハッシュ値を計算し、それを自身の秘密鍵で暗号化(または署名アルゴリズムに従って変換)して署名を生成します。
  • 署名検証: 受信者は、メッセージのハッシュ値を計算し、送信者の公開鍵を使って署名を復号化(または検証アルゴリズムに従って変換)します。両方のハッシュ値が一致すれば、署名は有効と判断されます。

3. パディングスキーム

RSAのようなブロック暗号では、メッセージのサイズが鍵のサイズと異なる場合や、特定の攻撃を防ぐために、暗号化や署名の前にメッセージに特定の構造(パディング)を追加します。

  • PKCS#1 v1.5 (RSASSA-PKCS1-v1_5): 広く使われてきたRSA署名パディングスキーム。メッセージのハッシュ値の前に、特定のバイト列(0x00 0x01 FF...FF 0x00)とハッシュアルゴリズムの識別子を追加します。シンプルですが、前述の通り脆弱性が指摘されています。
  • RSASSA-PSS (Probabilistic Signature Scheme): PKCS#1 v2.1で導入された、よりセキュアなパディングスキーム。ランダムなソルト値とマスク生成関数 (MGF) を使用して、署名に確率的な性質を持たせ、セキュリティを強化します。

4. ハッシュ関数

任意の長さの入力データから、固定長の出力(ハッシュ値またはメッセージダイジェスト)を生成する一方向関数です。SHA-1, SHA-256, MD5などが代表的です。デジタル署名では、メッセージ全体ではなくそのハッシュ値に署名することで効率を高めます。

5. マスク生成関数 (MGF1)

MGF1 (Mask Generation Function 1) は、PKCS#1で定義されている関数で、任意の長さのシードから任意の長さのマスクを生成するために使用されます。これは、ハッシュ関数を繰り返し適用することで実現されます。PSSでは、このMGF1がデータブロックの生成に不可欠な役割を果たします。

技術的詳細

RSASSA-PSSは、RFC 3447 (PKCS #1 v2.1) のセクション 9.1 で定義されています。その主要な構成要素は、EMSA-PSSエンコーディング操作とEMSA-PSS検証操作です。

EMSA-PSS エンコーディング操作 (emsaPSSEncode)

この関数は、署名されるメッセージのハッシュ値 mHash と、RSAモジュラスのビット長 emBits、ランダムなソルト salt、および使用するハッシュ関数 hash を入力として受け取り、エンコードされたメッセージ EM を生成します。

  1. 入力チェック: mHash の長さがハッシュ関数の出力長 hLen と一致するか、emLen (エンコードされたメッセージのバイト長) が hLen + sLen + 2 (ソルト長 sLen と定数) より小さい場合にエラーを返します。
  2. M' の生成: 8バイトのゼロプレフィックス、mHashsalt を連結して M' を生成します。 M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt
  3. H の計算: M' のハッシュ値 H = Hash(M') を計算します。
  4. DB の生成: emLen - sLen - hLen - 2 個のゼロオクテットからなるパディング文字列 PS と、0x01salt を連結して DB を生成します。 DB = PS || 0x01 || salt
  5. dbMask の生成と maskedDB: dbMask = MGF(H, emLen - hLen - 1) を計算し、DBdbMask のXORを取って maskedDB = DB XOR dbMask を生成します。ここで MGF はマスク生成関数 (MGF1) です。
  6. 最上位ビットのクリア: maskedDB の最上位オクテットの、8 * emLen - emBits ビットをゼロに設定します。これは、RSAモジュラスのビット長に合わせるための処理です。
  7. EM の生成: maskedDBH、およびトレーラーバイト 0xBC を連結して、最終的なエンコードされたメッセージ EM を生成します。 EM = maskedDB || H || 0xBC

EMSA-PSS 検証操作 (emsaPSSVerify)

この関数は、署名されるメッセージのハッシュ値 mHash、エンコードされたメッセージ em、RSAモジュラスのビット長 emBits、ソルト長 sLen、および使用するハッシュ関数 hash を入力として受け取り、署名が有効かどうかを検証します。

  1. 入力チェック: mHash の長さ、emLen の長さ、em の最後のバイトが 0xBC であるかなどを検証します。
  2. maskedDB と H の抽出: em から maskedDBH を抽出します。
  3. 最上位ビットのチェック: maskedDB の最上位オクテットの、8 * emLen - emBits ビットがすべてゼロであることを確認します。
  4. dbMask の生成と DB: dbMask = MGF(H, emLen - hLen - 1) を計算し、maskedDBdbMask のXORを取って DB = maskedDB XOR dbMask を復元します。
  5. ソルト長の自動検出 (PSSSaltLengthAuto): sLenPSSSaltLengthAuto の場合、DB の構造からソルト長を自動的に検出します。これは、0x01 バイトの位置を探すことで行われます。
  6. DB の構造検証: DB の先頭のゼロオクテットと 0x01 バイトが正しい位置にあることを確認します。
  7. salt の抽出: DB から salt を抽出します。
  8. M' の再生成と H' の計算: エンコーディング時と同様に、8バイトのゼロプレフィックス、mHash、抽出した salt を連結して M' を再生成し、そのハッシュ値 H' = Hash(M') を計算します。
  9. H と H' の比較: 抽出した H と計算した H' が一致するかどうかを比較します。一致すれば署名は有効です。

SignPSSVerifyPSS 関数

これらの関数は、上記のEMSA-PSS操作をRSA署名/検証プロセスに統合する高レベルAPIです。

  • SignPSS: 秘密鍵、ハッシュ関数、メッセージのハッシュ値、および PSSOptions (ソルト長を指定) を受け取り、RSASSA-PSS署名を生成します。ソルト長は PSSSaltLengthAuto (最大長) または PSSSaltLengthEqualsHash (ハッシュ長と同じ) に設定できます。
  • VerifyPSS: 公開鍵、ハッシュ関数、メッセージのハッシュ値、署名、および PSSOptions を受け取り、RSASSA-PSS署名を検証します。

PSSOptions

PSSOptions 構造体は、RSASSA-PSS署名生成および検証時のソルト長を制御するために導入されました。

  • SaltLength: ソルトの長さをバイト単位で指定します。
    • PSSSaltLengthAuto: 署名時には可能な限り最大のソルト長を使用し、検証時には自動検出します。
    • PSSSaltLengthEqualsHash: ソルト長をハッシュ関数の出力長と同じにします。

コアとなるコードの変更箇所

このコミットで追加された主要なファイルと関数は以下の通りです。

  • src/pkg/crypto/rsa/pss.go:

    • emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error): PSSエンコーディングロジック。
    • emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error: PSS検証ロジック。
    • signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error): 指定されたソルトでPSS署名を生成する内部関数。
    • PSSSaltLengthAuto (定数): ソルト長を自動検出または最大長にするための定数。
    • PSSSaltLengthEqualsHash (定数): ソルト長をハッシュ長と同じにするための定数。
    • PSSOptions (構造体): PSS署名/検証オプションを保持。
    • SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) (s []byte, err error): PSS署名を生成する公開API。
    • VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts *PSSOptions) error: PSS署名を検証する公開API。
    • verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error: 指定されたソルト長でPSS署名を検証する内部関数。
  • src/pkg/crypto/rsa/pss_test.go:

    • TestEMSAPSS(t *testing.T): 基本的なEMSA-PSSエンコーディング/検証のテスト。
    • TestPSSGolden(t *testing.T): PKCS#1 v2.1のテストベクトル (pss-vect.txt.bz2) を使用したゴールデンテスト。
    • TestPSSOpenSSL(t *testing.T): OpenSSLで生成されたPSS署名の検証テスト。
    • TestPSSSigning(t *testing.T): 様々なソルト長設定でのPSS署名と検証の組み合わせテスト。
    • bigFromHex, intFromHex, fromHex (ヘルパー関数): テストデータ処理のためのユーティリティ。

コアとなるコードの解説

emsaPSSEncodeemsaPSSVerify

これらの関数は、RSASSA-PSSの核となるエンコーディングと検証のロジックを実装しています。RFC 3447のセクション 9.1.1 (EMSA-PSS-Encode) と 9.1.2 (EMSA-PSS-Verify) のステップに厳密に従っています。

  • emsaPSSEncode:

    • hLen := hash.Size(): 使用するハッシュ関数の出力長を取得。
    • emLen := (emBits + 7) / 8: RSAモジュラスのビット長 emBits から、エンコードされたメッセージ EM のバイト長を計算。
    • db := em[:emLen-sLen-hLen-2+1+sLen]: DB (Data Block) の領域を確保。
    • h := em[emLen-sLen-hLen-2+1+sLen : emLen-1]: H (ハッシュ値) の領域を確保。
    • hash.Write(prefix[:]), hash.Write(mHash), hash.Write(salt): M' を構成する要素をハッシュ関数に書き込み。
    • h = hash.Sum(h[:0]): M' のハッシュ値 H を計算。
    • db[emLen-sLen-hLen-2] = 0x01: DB 内の 0x01 バイトを設定。
    • copy(db[emLen-sLen-hLen-1:], salt): DB 内にソルトをコピー。
    • mgf1XOR(db, hash, h): DBMGF1(H) をXORして maskedDB を生成。
    • db[0] &= (0xFF >> uint(8*emLen-emBits)): 最上位オクテットの不要なビットをクリア。
    • em[emLen-1] = 0xBC: トレーラーバイト 0xBC を設定。
  • emsaPSSVerify:

    • エンコーディングの逆操作を行い、DB を復元し、そこからソルトを抽出します。
    • 抽出したソルトと元のメッセージハッシュから M' を再構成し、そのハッシュ値 H' を計算します。
    • 最後に、元の署名から抽出した H と計算した H' を比較し、一致すれば検証成功とします。
    • if sLen == PSSSaltLengthAuto: ソルト長が自動検出の場合のロジック。DB 内の 0x01 バイトの位置からソルト長を特定します。

SignPSSVerifyPSS

これらの関数は、emsaPSSEncodeemsaPSSVerify を利用して、RSA秘密鍵/公開鍵を用いた実際の署名と検証を行います。

  • SignPSS:

    • saltLength := opts.saltLength(): PSSOptions からソルト長を取得。PSSSaltLengthAutoPSSSaltLengthEqualsHash の場合は実際のバイト長に変換。
    • salt := make([]byte, saltLength): 指定された長さのソルトを生成。
    • io.ReadFull(rand, salt): 暗号論的に安全な乱数ジェネレータ rand からソルトを生成。
    • emsaPSSEncode(hashed, nBits-1, salt, hash.New()): エンコードされたメッセージ EM を生成。
    • decrypt(rand, priv, m): RSA秘密鍵で EM を復号化(RSA署名操作)。
    • copyWithLeftPad(s, c.Bytes()): 署名結果を適切なバイト長にパディング。
  • VerifyPSS:

    • encrypt(new(big.Int), pub, s): RSA公開鍵で署名 s を暗号化(RSA検証操作)。
    • emsaPSSVerify(hashed, em, emBits, saltLen, hash.New()): 復号化された EM を用いてPSS検証を実行。

これらの関数は、Goの crypto/rsa パッケージの既存のRSA暗号プリミティブ (decrypt, encrypt, copyWithLeftPad) と組み合わせて、RSASSA-PSSの完全な実装を提供しています。

関連リンク

参考にした情報源リンク