[インデックス 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のサポートを追加するために、以下の主要な変更を導入しています。
-
TLS 1.2バージョンの定義と最大バージョンの更新:
VersionTLS12 = 0x0303がcommon.goに追加され、TLS 1.2のバージョン番号が定義されました。maxVersionがVersionTLS11からVersionTLS12に更新され、GoのTLS実装がTLS 1.2をサポートするようになりました。
-
署名アルゴリズムとハッシュ関数の導入:
- TLS 1.2では、ハンドシェイクメッセージの署名や証明書の検証において、使用するハッシュ関数と署名アルゴリズムの組み合わせを明示的に指定する必要があります。
common.goにextensionSignatureAlgorithms(拡張番号 13) が追加されました。これはClientHelloメッセージでクライアントがサポートする署名アルゴリズムをサーバーに通知するために使用されます。hashSHA1,hashSHA256,signatureRSA,signatureECDSAといった定数が定義され、TLS 1.2で利用可能なハッシュ関数と署名アルゴリズムが示されました。signatureAndHash構造体が導入され、ハッシュと署名の組み合わせを表現します。supportedSignatureAlgorithms変数が定義され、GoのTLS実装がサポートする署名アルゴリズムのリスト(現時点ではSHA256withRSAのみ)が設定されました。
-
ハンドシェイクメッセージの変更:
clientHelloMsg構造体にsignatureAndHashes []signatureAndHashフィールドが追加されました。これにより、クライアントはTLS 1.2でサポートする署名アルゴリズムをサーバーに通知できます。certificateRequestMsg構造体にhasSignatureAndHash boolとsignatureAndHashes []signatureAndHashフィールドが追加されました。サーバーがクライアント証明書を要求する際に、クライアントが使用すべき署名アルゴリズムを指定できるようになります。certificateVerifyMsg構造体にhasSignatureAndHash boolとsignatureAndHash signatureAndHashフィールドが追加されました。TLS 1.2では、このメッセージに署名に使用されたハッシュと署名アルゴリズムの情報が含まれるようになりました。
-
鍵交換 (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以降)を選択的に使用するようになりました。
-
擬似乱数関数 (PRF) の更新:
prf.goにsha256Hash関数が追加され、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が適用されるようになりました。
-
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)。maxVersionをVersionTLS12に更新。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関数内でcertificateRequestMsgとcertificateVerifyMsgを初期化する際に、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構造体にhasSignatureAndHashとsignatureAndHashesフィールドを追加し、マーシャル/アンマーシャルロジックを更新。certificateVerifyMsg構造体にhasSignatureAndHashとsignatureAndHashフィールドを追加し、マーシャル/アンマーシャルロジックを更新。
-
src/pkg/crypto/tls/handshake_server.go:doFullHandshake関数内で、サーバーがクライアント証明書を要求する際に、TLS 1.2以上であればcertReq.hasSignatureAndHashとcertReq.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ハッシュをオプションとして扱うように変更。finishedHashのWrite,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: https://datatracker.ietf.org/doc/html/rfc5246
- Go言語の
crypto/tlsパッケージのドキュメント: https://pkg.go.dev/crypto/tls (コミット当時のバージョンとは異なる可能性があります)
参考にした情報源リンク
- RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2
- Go言語の
crypto/tlsパッケージのソースコード (コミット当時のもの) - TLSプロトコルに関する一般的な情報源 (例: Wikipedia, MDN Web Docs)
- TLS 1.2の変更点に関する技術ブログや記事