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

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

このコミットは、Go言語の crypto/tls パッケージにTLS 1.2プロトコルのサポートを実装するものです。ただし、AES-GCM暗号スイートは含まれておらず、ハンドシェイクと証明書署名ハッシュはSHA-256に限定されています。

コミット

commit 7e767791b983d0260773b98135a37a86f5fc9712
Author: Adam Langley <agl@golang.org>
Date:   Tue Jul 2 19:58:56 2013 -0400

    crypto/tls: implement TLS 1.2.
    
    This does not include AES-GCM yet. Also, it assumes that the handshake and
    certificate signature hash are always SHA-256, which is true of the ciphersuites
    that we currently support.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/10762044

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

https://github.com/golang/go/commit/7e767791b983d0260773b98135a37a86f5fc9712

元コミット内容

crypto/tls: implement TLS 1.2.

このコミットは、TLS 1.2を実装するものです。 まだAES-GCMは含まれていません。また、ハンドシェイクと証明書の署名ハッシュは常にSHA-256であると仮定しています。これは現在サポートしている暗号スイートにおいては真です。

変更の背景

この変更の背景には、よりセキュアな通信プロトコルへの移行というTLSの進化があります。TLS 1.0およびTLS 1.1には、POODLE攻撃(Padding Oracle On Downgraded Legacy Encryption)やBEAST攻撃(Browser Exploit Against SSL/TLS)などの既知の脆弱性が存在しました。これらの攻撃は、古いプロトコルバージョンや特定の暗号スイートの脆弱性を悪用して、暗号化された通信の内容を解読する可能性がありました。

TLS 1.2は、これらの脆弱性に対処し、より強力な暗号化アルゴリズムとハッシュ関数を導入することで、セキュリティを大幅に向上させました。特に、MD5とSHA-1の組み合わせに依存していた以前のバージョンとは異なり、TLS 1.2ではより強力なハッシュ関数(SHA-256など)の使用が義務付けられています。このコミットは、Go言語の標準ライブラリが最新のセキュリティ標準に準拠し、より安全な通信を提供できるようにするための重要なステップでした。

前提知識の解説

  • TLS (Transport Layer Security): インターネット上でデータを安全にやり取りするための暗号化プロトコルです。以前はSSL (Secure Sockets Layer) と呼ばれていました。クライアントとサーバー間で安全な通信チャネルを確立するために使用されます。
  • TLSバージョン: TLSプロトコルには複数のバージョンがあり、新しいバージョンほどセキュリティが強化されています。
    • TLS 1.0 (RFC 2246): 1999年にリリース。MD5とSHA-1の組み合わせをPRNG (Pseudo-Random Number Generator) やFinishedメッセージのハッシュに使用していました。
    • TLS 1.1 (RFC 4346): 2006年にリリース。TLS 1.0のマイナーアップデートで、主にCBCモードの脆弱性(BEAST攻撃)に対処するための変更が含まれました。
    • TLS 1.2 (RFC 5246): 2008年にリリース。MD5とSHA-1の組み合わせに代わり、より強力なハッシュ関数(SHA-256など)の使用を義務付け、署名アルゴリズムの選択肢を増やしました。このバージョンから、ハンドシェイクメッセージのハッシュや証明書の署名にSHA-256が推奨されるようになりました。
  • ハンドシェイクプロトコル: TLS接続の確立時にクライアントとサーバー間で行われる一連のメッセージ交換です。このプロセスで、プロトコルバージョン、暗号スイート、鍵交換アルゴリズム、証明書などがネゴシエートされます。
  • Finishedメッセージ: ハンドシェイクの最後に送信されるメッセージで、ハンドシェイク全体の整合性を検証するために使用されます。このメッセージには、ハンドシェイクメッセージのハッシュが含まれます。
  • PRNG (Pseudo-Random Number Generator): 擬似乱数生成器。TLSでは、セッション鍵の生成など、暗号学的に安全な乱数が必要な場面で使用されます。
  • 暗号スイート (Cipher Suite): TLS接続で使用される鍵交換アルゴリズム、認証アルゴリズム、暗号化アルゴリズム、ハッシュアルゴリズムの組み合わせを定義します。例: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
  • 鍵交換アルゴリズム (Key Agreement Algorithm): クライアントとサーバーが共有秘密鍵を安全に確立するためのアルゴリズム。例: RSA、ECDHE (Elliptic Curve Diffie-Hellman Ephemeral)。
  • 署名アルゴリズム (Signature Algorithm): 証明書の検証やハンドシェイクメッセージの署名に使用されるアルゴリズム。TLS 1.2では、ハッシュアルゴリズムと署名アルゴリズムの組み合わせ(例: SHA256withRSA)が明示的に指定されるようになりました。
  • crypto.Hash: Go言語の crypto パッケージで定義されているハッシュ関数の識別子。

技術的詳細

このコミットは、Go言語の crypto/tls パッケージにTLS 1.2のサポートを追加するために、以下の主要な変更を導入しています。

  1. TLS 1.2バージョンの定義と最大バージョンの更新:

    • VersionTLS12 = 0x0303common.go に追加され、TLS 1.2のバージョン番号が定義されました。
    • maxVersionVersionTLS11 から VersionTLS12 に更新され、GoのTLS実装がTLS 1.2をサポートするようになりました。
  2. 署名アルゴリズムとハッシュ関数の導入:

    • TLS 1.2では、ハンドシェイクメッセージの署名や証明書の検証において、使用するハッシュ関数と署名アルゴリズムの組み合わせを明示的に指定する必要があります。
    • common.goextensionSignatureAlgorithms (拡張番号 13) が追加されました。これはClientHelloメッセージでクライアントがサポートする署名アルゴリズムをサーバーに通知するために使用されます。
    • hashSHA1, hashSHA256, signatureRSA, signatureECDSA といった定数が定義され、TLS 1.2で利用可能なハッシュ関数と署名アルゴリズムが示されました。
    • signatureAndHash 構造体が導入され、ハッシュと署名の組み合わせを表現します。
    • supportedSignatureAlgorithms 変数が定義され、GoのTLS実装がサポートする署名アルゴリズムのリスト(現時点では SHA256withRSA のみ)が設定されました。
  3. ハンドシェイクメッセージの変更:

    • clientHelloMsg 構造体に signatureAndHashes []signatureAndHash フィールドが追加されました。これにより、クライアントはTLS 1.2でサポートする署名アルゴリズムをサーバーに通知できます。
    • certificateRequestMsg 構造体に hasSignatureAndHash boolsignatureAndHashes []signatureAndHash フィールドが追加されました。サーバーがクライアント証明書を要求する際に、クライアントが使用すべき署名アルゴリズムを指定できるようになります。
    • certificateVerifyMsg 構造体に hasSignatureAndHash boolsignatureAndHash signatureAndHash フィールドが追加されました。TLS 1.2では、このメッセージに署名に使用されたハッシュと署名アルゴリズムの情報が含まれるようになりました。
  4. 鍵交換 (Key Agreement) の変更:

    • cipherSuite 構造体の ka フィールド(鍵交換アルゴリズムを生成する関数)のシグネチャが func() keyAgreement から func(version uint16) keyAgreement に変更されました。これにより、鍵交換アルゴリズムの生成時にTLSバージョンを考慮できるようになりました。
    • ecdheRSAKeyAgreement 構造体に version uint16 フィールドが追加され、TLSバージョンに応じた処理が可能になりました。
    • hashForServerKeyExchange 関数が導入され、TLSバージョンに基づいてMD5+SHA1ハッシュ(TLS 1.1以前)またはSHA-256ハッシュ(TLS 1.2以降)を選択的に使用するようになりました。
  5. 擬似乱数関数 (PRF) の更新:

    • prf.gosha256Hash 関数が追加され、SHA-256を使用したハッシュ計算が可能になりました。
    • prf12 関数が追加され、TLS 1.2のPRF(SHA-256ベース)を実装しました。
    • prfForVersion 関数が導入され、TLSバージョンに応じて適切なPRF(SSL 3.0のprf30、TLS 1.0/1.1のprf10、TLS 1.2のprf12)を選択するようになりました。
    • masterFromPreMasterSecret および keysFromMasterSecret 関数が prfForVersion を使用するように変更され、TLSバージョンに応じたPRFが適用されるようになりました。
  6. Finishedメッセージのハッシュ計算の変更:

    • finishedHash 構造体が変更され、TLS 1.2ではSHA-256のみを使用するように簡素化されました。TLS 1.1以前ではMD5とSHA-1の両方が引き続き使用されます。
    • clientSum および serverSum メソッドがTLSバージョンに応じて異なるハッシュ計算ロジックを使用するように更新されました。TLS 1.2ではSHA-256ベースのPRFが使用されます。
    • hashForClientCertificate 関数が追加され、クライアント証明書の署名に使用するハッシュとハッシュ関数識別子をTLSバージョンに応じて提供するようになりました。

これらの変更により、GoのTLS実装はTLS 1.2プロトコルの主要な要件を満たし、より現代的な暗号化標準に対応できるようになりました。

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

このコミットで変更された主要なファイルと、その中の重要な変更箇所は以下の通りです。

  • src/pkg/crypto/tls/common.go:

    • VersionTLS12 定数の追加 (0x0303)。
    • maxVersionVersionTLS12 に更新。
    • extensionSignatureAlgorithms (拡張番号 13) の追加。
    • TLS 1.2用のハッシュ関数 (hashSHA1, hashSHA256) と署名アルゴリズム (signatureRSA, signatureECDSA) の定数定義。
    • signatureAndHash 構造体の定義。
    • supportedSignatureAlgorithms 変数の定義。
  • src/pkg/crypto/tls/cipher_suites.go:

    • cipherSuite 構造体の ka フィールドの型を func() keyAgreement から func(version uint16) keyAgreement に変更。
    • rsaKA および ecdheRSAKA 関数のシグネチャ変更と、ecdheRSAKeyAgreement の初期化時にバージョンを渡すように変更。
  • src/pkg/crypto/tls/conn.go:

    • readHandshake 関数内で certificateRequestMsgcertificateVerifyMsg を初期化する際に、hasSignatureAndHash フィールドを c.vers >= VersionTLS12 に基づいて設定するように変更。
  • src/pkg/crypto/tls/handshake_client.go:

    • clientHandshake 関数内で、ClientHelloメッセージのバージョンがTLS 1.2以上の場合に hello.signatureAndHashes を設定するように変更。
    • finishedHash の初期化を c.vers に基づいて行うように変更。
    • certificateVerify メッセージの署名生成時に、finishedHash.hashForClientCertificate() を使用してハッシュとハッシュ関数識別子を取得するように変更。
  • src/pkg/crypto/tls/handshake_messages.go:

    • clientHelloMsg 構造体に signatureAndHashes フィールドを追加し、マーシャル/アンマーシャルロジックを更新。
    • certificateRequestMsg 構造体に hasSignatureAndHashsignatureAndHashes フィールドを追加し、マーシャル/アンマーシャルロジックを更新。
    • certificateVerifyMsg 構造体に hasSignatureAndHashsignatureAndHash フィールドを追加し、マーシャル/アンマーシャルロジックを更新。
  • src/pkg/crypto/tls/handshake_server.go:

    • doFullHandshake 関数内で、サーバーがクライアント証明書を要求する際に、TLS 1.2以上であれば certReq.hasSignatureAndHashcertReq.signatureAndHashes を設定するように変更。
    • クライアント証明書の検証時に、hs.finishedHash.hashForClientCertificate() を使用してハッシュとハッシュ関数識別子を取得するように変更。
  • src/pkg/crypto/tls/key_agreement.go:

    • sha256.New のインポートを追加。
    • sha256Hash 関数を追加。
    • hashForServerKeyExchange 関数を追加し、TLSバージョンに応じてMD5+SHA1またはSHA-256ハッシュを選択。
    • ecdheRSAKeyAgreement 構造体に version フィールドを追加。
    • generateServerKeyExchange および processServerKeyExchange 関数内で、TLSバージョンに応じたハッシュ関数と署名アルゴリズムを使用するように変更。
  • src/pkg/crypto/tls/prf.go:

    • crypto/sha256 のインポートを追加。
    • prf12 関数を追加し、TLS 1.2のPRFを実装。
    • prfForVersion 関数を追加し、TLSバージョンに応じて適切なPRF関数を返すように変更。
    • masterFromPreMasterSecret および keysFromMasterSecret 関数が prfForVersion を使用するように変更。
    • newFinishedHash 関数がTLS 1.2以上の場合にSHA-256ハッシュを使用するように変更。
    • finishedHash 構造体を簡素化し、TLS 1.2以前のMD5ハッシュをオプションとして扱うように変更。
    • finishedHashWrite, clientSum, serverSum, hashForClientCertificate メソッドをTLSバージョンに応じて更新。

コアとなるコードの解説

このコミットの核心は、TLS 1.2で導入されたハッシュ関数と署名アルゴリズムの柔軟性に対応することです。

1. common.go におけるバージョンと署名アルゴリズムの定義:

// common.go
const (
	VersionSSL30 = 0x0300
	VersionTLS10 = 0x0301
	VersionTLS11 = 0x0302
	VersionTLS12 = 0x0303 // 新しく追加
)

const (
	minVersion = VersionSSL30
	maxVersion = VersionTLS12 // 更新
)

// TLS extension numbers
var (
	// ... 既存の拡張 ...
	extensionSignatureAlgorithms uint16 = 13 // 新しく追加
)

// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1)
const (
	hashSHA1   uint8 = 2
	hashSHA256 uint8 = 4 // SHA-256の定義
)

// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1)
const (
	signatureRSA   uint8 = 1
	signatureECDSA uint8 = 3
)

// signatureAndHash mirrors the TLS 1.2, SignatureAndHashAlgorithm struct.
type signatureAndHash struct {
	hash, signature uint8
}

// supportedSignatureAlgorithms contains the signature and hash algorithms that
// the code will adverse as supported both in a TLS 1.2 ClientHello and
// CertificateRequest.
var supportedSignatureAlgorithms = []signatureAndHash{
	{hashSHA256, signatureRSA}, // SHA256withRSAをサポート
}

VersionTLS12 の追加と maxVersion の更新により、GoのTLS実装がTLS 1.2を認識し、ネゴシエートできるようになります。extensionSignatureAlgorithms は、クライアントがサポートするハッシュと署名の組み合わせをサーバーに伝えるための重要な拡張です。signatureAndHash 構造体と supportedSignatureAlgorithms は、TLS 1.2の新しい要件である「ハッシュと署名のペア」を表現します。

2. prf.go におけるPRFのバージョン管理:

// prf.go
// sha256Hash implements TLS 1.2's hash function.
func sha256Hash(slices [][]byte) []byte {
	h := sha256.New()
	for _, slice := range slices {
		h.Write(slice)
	}
	return h.Sum(nil)
}

// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, section 5.
func prf12(result, secret, label, seed []byte) {
	labelAndSeed := make([]byte, len(label)+len(seed))
	copy(labelAndSeed, label)
	copy(labelAndSeed[len(label):], seed)

	pHash(result, secret, labelAndSeed, sha256.New) // SHA-256を使用
}

func prfForVersion(version uint16) func(result, secret, label, seed []byte) {
	switch version {
	case VersionSSL30:
		return prf30
	case VersionTLS10, VersionTLS11:
		return prf10
	case VersionTLS12:
		return prf12 // TLS 1.2ではprf12を使用
	default:
		panic("unknown version")
	}
}

// finishedHash 構造体の変更
type finishedHash struct {
	client hash.Hash // TLS 1.2ではSHA-256
	server hash.Hash // TLS 1.2ではSHA-256

	// Prior to TLS 1.2, an additional MD5 hash is required.
	clientMD5 hash.Hash // TLS 1.1以前でのみ使用
	serverMD5 hash.Hash // TLS 1.1以前でのみ使用

	version uint16
}

func newFinishedHash(version uint16) finishedHash {
	if version >= VersionTLS12 {
		return finishedHash{sha256.New(), sha256.New(), nil, nil, version} // TLS 1.2ではSHA-256のみ
	}
	return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), version} // TLS 1.1以前ではMD5とSHA-1
}

// finishedHash.clientSum および serverSum メソッドの更新
func (h finishedHash) clientSum(masterSecret []byte) []byte {
	// ...
	if h.version >= VersionTLS12 {
		seed := h.client.Sum(nil)
		prf12(out, masterSecret, clientFinishedLabel, seed) // TLS 1.2ではprf12
	} else {
		// ... 既存のprf10ロジック ...
	}
	return out
}

// finishedHash.hashForClientCertificate メソッドの追加
func (h finishedHash) hashForClientCertificate() ([]byte, crypto.Hash) {
	if h.version >= VersionTLS12 {
		digest := h.server.Sum(nil)
		return digest, crypto.SHA256 // TLS 1.2ではSHA-256
	}

	digest := make([]byte, 0, 36)
	digest = h.serverMD5.Sum(digest)
	digest = h.server.Sum(digest) // h.serverはSHA1
	return digest, crypto.MD5SHA1 // TLS 1.1以前ではMD5+SHA1
}

prf.go の変更は、TLS 1.2の主要なセキュリティ強化であるハッシュ関数の選択を管理します。prf12 はSHA-256をベースにしたPRFを実装し、prfForVersion はネゴシエートされたTLSバージョンに基づいて適切なPRFを選択します。finishedHash 構造体と関連メソッドの変更は、Finishedメッセージのハッシュ計算がTLS 1.2ではSHA-256のみを使用し、以前のバージョンではMD5とSHA-1の組み合わせを使用するという仕様に対応しています。特に hashForClientCertificate は、クライアント証明書の署名時に使用するハッシュアルゴリズムをTLSバージョンに応じて適切に選択する役割を担います。

これらの変更により、GoのTLS実装は、TLS 1.2のセキュリティ要件を満たし、より堅牢な暗号化通信を提供できるようになりました。

関連リンク

参考にした情報源リンク

  • RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2
  • Go言語の crypto/tls パッケージのソースコード (コミット当時のもの)
  • TLSプロトコルに関する一般的な情報源 (例: Wikipedia, MDN Web Docs)
  • TLS 1.2の変更点に関する技術ブログや記事