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

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

このコミットでは、src/pkg/crypto/crypto.gosrc/pkg/crypto/tls/common.gosrc/pkg/crypto/tls/handshake_client.gosrc/pkg/crypto/tls/key_agreement.go の4つのファイルが変更されています。

コミット

  • コミットハッシュ: 2ca4a61658b5561cc807fc1cebe177169ff28034
  • Author: Adam Langley agl@golang.org
  • Date: Mon Dec 19 10:39:30 2011 -0500
  • コミットメッセージ:
    crypto/tls: don't assume an RSA private key in the API.
    
    We still very much assume it in the code, but with this change in
    place we can implement other things later without changing and users
    of the package.
    
    Fixes #2319.
    
    R=golang-dev, bradfitz, r
    CC=golang-dev
    https://golang.org/cl/5489073
    

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

https://github.com/golang/go/commit/2ca4a61658b5561cc807fc1cebe177169ff28034

元コミット内容

crypto/tls: don't assume an RSA private key in the API.

We still very much assume it in the code, but with this change in
place we can implement other things later without changing and users
of the package.

Fixes #2319.

R=golang-dev, bradfitz, r
CC=golang-dev
https://golang.org/cl/5489073

変更の背景

このコミットの背景には、Go言語の crypto/tls パッケージが、TLS (Transport Layer Security) プロトコルにおける秘密鍵の型として、RSA秘密鍵のみを前提としていたという問題があります。これは、Issue #2319「TLS interface only supports RSA keys」として報告されていました。

TLSプロトコルでは、サーバー認証や鍵交換のために秘密鍵が使用されますが、RSA鍵以外にもECDSA (Elliptic Curve Digital Signature Algorithm) やEd25519などの異なるアルゴリズムに基づく秘密鍵が存在します。しかし、当時の crypto/tls パッケージのAPI設計では、Certificate 構造体の PrivateKey フィールドが *rsa.PrivateKey 型に固定されており、これにより他の種類の秘密鍵を扱うことができませんでした。

この制限は、将来的にRSA以外の鍵アルゴリズムをサポートしようとした際に、APIの破壊的変更を伴う可能性がありました。そのため、APIレベルでRSA秘密鍵への依存を解消し、より汎用的な秘密鍵の型を許容するように変更することが求められました。これにより、コード内部では引き続きRSA秘密鍵を前提とする部分が残るものの、APIの利用者にとっては将来的な拡張性を持たせることが可能になります。

前提知識の解説

crypto/tls パッケージ

crypto/tls はGo言語の標準ライブラリの一部で、TLS (Transport Layer Security) プロトコルを実装するためのパッケージです。TLSは、インターネット上での安全な通信を確立するために広く使用されており、ウェブブラウザとサーバー間のHTTPS通信などで利用されています。このパッケージは、TLSクライアントとサーバーの実装、証明書の管理、鍵交換、暗号化などの機能を提供します。

RSA秘密鍵

RSA (Rivest–Shamir–Adleman) は、公開鍵暗号方式の一つで、デジタル署名や鍵交換に広く用いられています。RSA秘密鍵は、RSA公開鍵とペアになる鍵で、暗号化されたデータの復号やデジタル署名の生成に使用されます。Go言語の crypto/rsa パッケージで扱われます。

Go言語の interface

Go言語の interface は、メソッドのシグネチャの集合を定義する型です。ある型がインターフェースで定義されたすべてのメソッドを実装していれば、その型はそのインターフェースを満たすとみなされます。これにより、具体的な型に依存しない汎用的なコードを書くことが可能になります。例えば、io.Reader インターフェースは Read メソッドを持つ任意の型を受け入れることができます。

型アサーション (Type Assertion)

Go言語の型アサーションは、インターフェース型の値が、特定の具象型であるかどうかをチェックし、もしそうであればその具象型の値として取り出すための構文です。 value.(Type) の形式で記述され、valueType 型であれば、その値が返されます。もし valueType 型でなければ、パニックが発生します。安全な形式として value, ok := value.(Type) があり、この場合 ok は型アサーションが成功したかどうかを示すブール値になります。

このコミットでは、crypto.PrivateKey インターフェース型として受け取った秘密鍵を、内部処理で *rsa.PrivateKey 型として扱うために型アサーションが使用されています。

技術的詳細

このコミットの主要な技術的変更は、crypto/tls パッケージのAPIがRSA秘密鍵に強く依存している状態を緩和することです。具体的には、以下の点が変更されています。

  1. crypto.PrivateKey インターフェースの導入: src/pkg/crypto/crypto.go に、PrivateKey interface{} という空のインターフェースが導入されました。これは、任意の型を秘密鍵として表現できる汎用的な型となります。

  2. Certificate 構造体の PrivateKey フィールドの型変更: src/pkg/crypto/tls/common.go 内の Certificate 構造体において、PrivateKey フィールドの型が *rsa.PrivateKey から新しく定義された crypto.PrivateKey インターフェース型に変更されました。これにより、Certificate 構造体はRSA秘密鍵だけでなく、将来的に他のアルゴリズムの秘密鍵も保持できるようになります。コメントには // supported types: *rsa.PrivateKey と追記され、現時点ではRSA秘密鍵のみがサポートされていることが明示されています。

  3. 内部コードでの型アサーションの利用: src/pkg/crypto/tls/handshake_client.gosrc/pkg/crypto/tls/key_agreement.go の内部処理では、Certificate.PrivateKeycrypto.PrivateKey 型になったため、RSA秘密鍵に特化した関数 (rsa.SignPKCS1v15, rsa.DecryptPKCS1v15SessionKey) に渡す際に、.(*rsa.PrivateKey) という型アサーションを使用して、crypto.PrivateKey 型の値を *rsa.PrivateKey 型に変換しています。これは、APIは汎用化されたものの、実際の暗号処理のコードはまだRSA秘密鍵に依存しているためです。

この変更により、crypto/tls パッケージの利用者は、将来的にRSA以外の秘密鍵を扱う際に、Certificate 構造体の定義自体が変わることを心配する必要がなくなりました。パッケージの内部実装はまだRSAに特化していますが、APIの柔軟性が向上し、将来の拡張が容易になっています。

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

src/pkg/crypto/crypto.go

--- a/src/pkg/crypto/crypto.go
+++ b/src/pkg/crypto/crypto.go
@@ -71,3 +71,6 @@ func RegisterHash(h Hash, f func() hash.Hash) {
 	}\n \thashes[h] = f\n }\n+\n+// PrivateKey represents a private key using an unspecified algorithm.\n+type PrivateKey interface{}\n```

### `src/pkg/crypto/tls/common.go`

```diff
--- a/src/pkg/crypto/tls/common.go
+++ b/src/pkg/crypto/tls/common.go
@@ -5,8 +5,8 @@
 package tls
 
 import (
+\t"crypto"
 \t"crypto/rand"
-\t"crypto/rsa"
 \t"crypto/x509"
 \t"io"
 \t"strings"
@@ -255,7 +255,7 @@ func (c *Config) BuildNameToCertificate() {
 // A Certificate is a chain of one or more certificates, leaf first.
 type Certificate struct {
 	Certificate [][]byte
-\tPrivateKey  *rsa.PrivateKey
+\tPrivateKey  crypto.PrivateKey // supported types: *rsa.PrivateKey
 	// OCSPStaple contains an optional OCSP response which will be served
 	// to clients that request it.
 	OCSPStaple []byte

src/pkg/crypto/tls/handshake_client.go

--- a/src/pkg/crypto/tls/handshake_client.go
+++ b/src/pkg/crypto/tls/handshake_client.go
@@ -234,7 +234,7 @@ func (c *Conn) clientHandshake() error {
 		digest := make([]byte, 0, 36)
 		digest = finishedHash.serverMD5.Sum(digest)
 		digest = finishedHash.serverSHA1.Sum(digest)
-\t\tsigned, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey, crypto.MD5SHA1, digest)
+\t\tsigned, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest)
 		if err != nil {
 			return c.sendAlert(alertInternalError)
 		}

src/pkg/crypto/tls/key_agreement.go

--- a/src/pkg/crypto/tls/key_agreement.go
+++ b/src/pkg/crypto/tls/key_agreement.go
@@ -44,7 +44,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe
 		ciphertext = ckx.ciphertext[2:]
 	}
 
-\terr = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret)
+\terr = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
 	if err != nil {
 		return nil, err
 	}
@@ -147,7 +147,7 @@ Curve:
 	copy(serverECDHParams[4:], ecdhePublic)
 
 	md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams)
-\tsig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, crypto.MD5SHA1, md5sha1)
+\tsig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1)
 	if err != nil {
 		return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
 	}

コアとなるコードの解説

src/pkg/crypto/crypto.go の変更

  • type PrivateKey interface{} が追加されました。これは、Go言語の crypto パッケージ全体で利用可能な、秘密鍵を表すための汎用的なインターフェースです。このインターフェースはメソッドを持たないため、任意の型がこのインターフェースを満たすことができます。これにより、秘密鍵の具体的な型に依存しないAPI設計が可能になります。

src/pkg/crypto/tls/common.go の変更

  • import "crypto/rsa" が削除され、代わりに import "crypto" が追加されました。これは、Certificate 構造体の PrivateKey フィールドの型が *rsa.PrivateKey から crypto.PrivateKey インターフェースに変更されたためです。
  • Certificate 構造体の PrivateKey フィールドの型が *rsa.PrivateKey から crypto.PrivateKey に変更されました。これにより、Certificate 構造体はRSA秘密鍵だけでなく、将来的に他のアルゴリズムの秘密鍵も保持できるようになります。
  • // supported types: *rsa.PrivateKey というコメントが追加され、現時点ではRSA秘密鍵のみがサポートされていることが明示されています。これは、APIは汎用化されたものの、内部実装はまだRSAに依存していることを示唆しています。

src/pkg/crypto/tls/handshake_client.go および src/pkg/crypto/tls/key_agreement.go の変更

  • これらのファイルでは、Certificate 構造体の PrivateKey フィールドが crypto.PrivateKey インターフェース型になったことに伴い、既存のRSA秘密鍵を扱う関数 (rsa.SignPKCS1v15rsa.DecryptPKCS1v15SessionKey) に渡す際に、型アサーション .(*rsa.PrivateKey) が追加されました。
  • 例えば、c.config.Certificates[0].PrivateKeycrypto.PrivateKey 型ですが、rsa.SignPKCS1v15 関数は *rsa.PrivateKey 型を期待するため、c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey) と記述することで、インターフェース型の値を具象型である *rsa.PrivateKey に変換しています。
  • この変更は、APIの柔軟性を高めつつも、既存のRSAに特化した暗号処理ロジックを維持するための過渡的な措置です。将来的に他の鍵アルゴリズムがサポートされる際には、これらの型アサーションを含むコードも、より汎用的なインターフェースベースの処理に置き換えられる可能性があります。

関連リンク

参考にした情報源リンク

  • Go crypto/tls パッケージの Certificate 構造体に関するドキュメント: https://pkg.go.dev/crypto/tls#Certificate (現在のドキュメントでは、PrivateKey フィールドが crypto.Signer インターフェースを実装する任意の型を受け入れることが明記されています。これは、このコミット以降のさらなる進化を示しています。)