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

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

このコミットは、Go言語のcrypto/tlsパッケージにおいて、TLSの再ネゴシエーション拡張(RFC 5746)のサポートを追加するものです。これにより、TLS再ネゴシエーション攻撃(CVE-2009-3555)に対する防御策が導入され、GoのTLS実装のセキュリティが強化されます。

コミット

commit 779ef7bd132ae4971f07baf2df8eec508a45f60c
Author: Adam Langley <agl@golang.org>
Date:   Thu Jan 9 13:38:11 2014 -0500

    crypto/tls: support renegotiation extension.
    
    The renegotiation extension was introduced[1] due to an attack by Ray in
    which a client's handshake was spliced into a connection that was
    renegotiating, thus giving an attacker the ability to inject an
    arbitary prefix into the connection.
    
    Go has never supported renegotiation as a server and so this attack
    doesn't apply. As a client, it's possible that at some point in the
    future the population of servers will be sufficiently updated that
    it'll be possible to reject connections where the server hasn't
    demonstrated that it has been updated to address this problem.
    
    We're not at that point yet, but it's good for Go servers to support
    the extension so that it might be possible to do in the future.
    
    [1] https://tools.ietf.org/search/rfc5746
    
    R=golang-codereviews, mikioh.mikioh
    CC=golang-codereviews
    https://golang.org/cl/48580043

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

https://github.com/golang/go/commit/779ef7bd132ae4971f07baf2df8eec508a45f60c

元コミット内容

crypto/tls: renegotiation extensionのサポート。

再ネゴシエーション拡張は、Rayによる攻撃[1]によって導入されました。この攻撃では、クライアントのハンドシェイクが再ネゴシエーション中の接続に組み込まれ、攻撃者が接続に任意のプレフィックスを挿入する能力を与えました。

Goはサーバーとして再ネゴシエーションをサポートしたことがないため、この攻撃は適用されません。クライアントとしては、将来的にサーバーの大部分がこの問題に対処するために十分に更新され、サーバーがこの問題に対処したことを示していない接続を拒否できるようになる可能性があります。

まだその段階ではありませんが、将来的に可能になるようにGoサーバーがこの拡張をサポートすることは良いことです。

[1] https://tools.ietf.org/search/rfc5746

変更の背景

このコミットの背景には、TLS/SSLプロトコルにおける再ネゴシエーションの脆弱性(CVE-2009-3555)があります。この脆弱性は、2009年に発見されたもので、攻撃者が既存のTLS接続に任意のデータを挿入できるというものでした。

具体的には、攻撃者はまずターゲットサーバーとのTLS接続を確立します。次に、その接続上で任意のプレーンテキストデータをサーバーに送信します。その後、攻撃者は正当なクライアントとサーバーの間で中間者(Man-in-the-Middle, MITM)として振る舞い、クライアントが接続を試みると、クライアントのTLSハンドシェイクを傍受し、それを自身の既存のサーバーとの接続に組み込み、再ネゴシエーションを開始させます。

サーバーはMITM攻撃に気づかず、クライアントのハンドシェイクを既存の接続上での正当な再ネゴシエーションとして扱います。その結果、サーバーは攻撃者が最初に挿入したデータが、現在通信している正当なクライアントから発信されたものだと誤解してしまいます。これにより、攻撃者はサーバーが正当なクライアントからのものと認識するアプリケーションプロトコルストリームにコマンドやデータを挿入することが可能になりました。例えば、HTTPの文脈では、攻撃者が部分的なリクエストを挿入し、サーバーがそれをクライアントのその後のリクエストと結合することで、不正な操作(例:セッションクッキーが関与する場合、攻撃者が正当なユーザーに代わって購入を行う)につながる可能性がありました。この脆弱性はSSL v3.0以降およびTLS v1.0以降に影響を与え、HTTP、IMAP、SMTPなどのプロトコルに影響を及ぼしました。

この脆弱性に対処するため、RFC 5746「TLS Renegotiation Indication Extension」が策定されました。このRFCは、再ネゴシエーションが実行されるTLS接続に暗号学的にバインドするメカニズムを導入することで、この種の攻撃を防ぐことを目的としています。

Go言語のTLS実装は、サーバーサイドでの再ネゴシエーションを元々サポートしていなかったため、この攻撃の直接的な影響は受けていませんでした。しかし、クライアントとしては、将来的にRFC 5746に対応したサーバーが増えることを考慮し、Goクライアントがこの拡張をサポートすることで、より安全な通信が可能になるという判断から、このコミットが導入されました。また、Goサーバーがこの拡張をサポートすることで、将来的にクライアントがサーバーのRFC 5746対応状況を確認し、非対応のサーバーとの接続を拒否できるようになる可能性も考慮されています。

前提知識の解説

TLS/SSLプロトコル

TLS (Transport Layer Security) およびその前身である SSL (Secure Sockets Layer) は、インターネット上で安全な通信を行うための暗号化プロトコルです。クライアントとサーバー間でデータを送受信する際に、盗聴、改ざん、なりすましを防ぐ役割を果たします。

TLS通信は、主に以下の2つのフェーズで構成されます。

  1. ハンドシェイクフェーズ (Handshake Phase):
    • クライアントとサーバーが互いを認証し、暗号スイート(使用する暗号アルゴリズムの組み合わせ)を決定し、セッションキーを生成します。
    • このフェーズで、サーバーは自身のデジタル証明書をクライアントに提示し、クライアントはそれを検証します。
    • セッションキーは、その後のデータ転送フェーズで使用される対称鍵暗号の鍵となります。
  2. データ転送フェーズ (Data Transfer Phase):
    • ハンドシェイクフェーズで確立されたセキュアなチャネルを通じて、アプリケーションデータが暗号化されて送受信されます。

TLS再ネゴシエーション

TLS再ネゴシエーションとは、確立済みのTLSセッション中に、新しいハンドシェイクを実行して、暗号スイートの変更、新しいセッションキーの生成、クライアント認証の追加などを行うプロセスです。これは、例えば、初期接続時にはクライアント認証を行わなかったが、特定の操作を行う際にクライアント認証が必要になった場合などに利用されます。

TLS再ネゴシエーション攻撃 (CVE-2009-3555)

前述の通り、この攻撃は、TLS再ネゴシエーションの際に、既存のセッションと新しいハンドシェイクの間に暗号学的な関連付けが不足していたことを悪用します。攻撃者は、自身の接続と正当なクライアントの接続を巧妙に組み合わせることで、サーバーが攻撃者のデータを正当なクライアントからのものと誤認するように仕向けました。

RFC 5746: TLS Renegotiation Indication Extension

このRFCは、TLS再ネゴシエーション攻撃に対処するために導入された拡張です。主な目的は、再ネゴシエーションを既存のTLS接続に暗号学的にバインドすることです。これにより、攻撃者が不正なデータを挿入しても、サーバーがそれを検出できるようになります。

RFC 5746は、以下の2つのメカニズムを導入しています。

  1. Renegotiation Info Extension (RIE):
    • クライアントとサーバーがハンドシェイクメッセージ(ClientHello, ServerHello)に含める新しいTLS拡張です。
    • この拡張には、以前のハンドシェイクから導出されたキー確認値(verify_data)が含まれます。
    • 再ネゴシエーションを行う際、クライアントとサーバーは、この拡張を交換し、verify_dataが一致するかどうかを確認します。これにより、両者が同じ過去の接続状態を認識していることを保証します。
  2. Signaling Cipher Suite Value (SCSV):
    • TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00FF) という特別な暗号スイート値です。
    • これは、クライアントがClientHelloメッセージに含めることで、サーバーに対してRFC 5746をサポートしていることを示唆します。
    • サーバーがこのSCSVを受け取った場合、サーバーもRFC 5746に対応している必要があります。

これらのメカニズムにより、再ネゴシエーションが特定の接続に暗号学的に結びつけられ、攻撃者がデータを挿入しても、その整合性が検証されるようになります。

技術的詳細

このコミットは、Go言語のcrypto/tlsパッケージにRFC 5746で定義されている再ネゴシエーション拡張のサポートを追加します。

extensionRenegotiationInfoscsvRenegotiationの追加

src/pkg/crypto/tls/common.goファイルに、再ネゴシエーション拡張のタイプを示す定数extensionRenegotiationInfo (0xff01) と、SCSVを示す定数scsvRenegotiation (0x00ff) が追加されました。これらは、TLSハンドシェイクメッセージ内で再ネゴシエーション関連の情報を識別するために使用されます。

clientHelloMsgserverHelloMsgの変更

src/pkg/crypto/tls/handshake_messages.goファイルでは、clientHelloMsgserverHelloMsg構造体にsecureRenegotiationというブール型のフィールドが追加されました。このフィールドは、クライアントまたはサーバーがセキュアな再ネゴシエーションをサポートしているかどうかを示します。

clientHelloMsgの変更点

  • marshal()メソッド:
    • secureRenegotiationtrueの場合、extensionRenegotiationInfo拡張がClientHelloメッセージに追加されるようになりました。この拡張は、長さが1バイトで、その値は0です。これは、RFC 5746で定義されているrenegotiated_connectionフィールドが空であることを示します。
    • 既存の拡張(extensionNextProtoNeg, extensionServerNameなど)のエンコーディングロジックも、バイトオーダーの修正(& 0xffの追加)が行われています。
  • unmarshal()メソッド:
    • 受信したClientHelloメッセージの暗号スイートリストにscsvRenegotiationが含まれている場合、secureRenegotiationフィールドがtrueに設定されます。
    • また、extensionRenegotiationInfo拡張がメッセージ内に存在し、その形式がRFC 5746の仕様(長さが1で値が0)に準拠している場合も、secureRenegotiationフィールドがtrueに設定されます。

serverHelloMsgの変更点

  • marshal()メソッド:
    • secureRenegotiationtrueの場合、extensionRenegotiationInfo拡張がServerHelloメッセージに追加されるようになりました。ClientHelloと同様に、長さ1、値0の形式です。
  • unmarshal()メソッド:
    • 受信したServerHelloメッセージにextensionRenegotiationInfo拡張が存在し、その形式がRFC 5746の仕様に準拠している場合、secureRenegotiationフィールドがtrueに設定されます。

クライアントハンドシェイクの変更

src/pkg/crypto/tls/handshake_client.goファイルでは、clientHandshake()関数内でclientHelloMsgを構築する際に、secureRenegotiationフィールドが常にtrueに設定されるようになりました。これは、Goクライアントが常にセキュアな再ネゴシエーションを試みることを意味します。

サーバーハンドシェイクの変更

src/pkg/crypto/tls/handshake_server.goファイルでは、サーバーがserverHelloMsgを構築する際に、クライアントのclientHelloMsgsecureRenegotiationフィールドの値を自身のserverHelloMsgsecureRenegotiationフィールドにコピーするようになりました。これにより、サーバーはクライアントがセキュアな再ネゴシエーションをサポートしていることを認識し、それに応じて応答します。

テストデータの更新

このコミットでは、多数のテストデータファイル(src/pkg/crypto/tls/testdata/以下のファイル)が更新されています。これらのファイルは、TLSハンドシェイクのバイトストリームを記録したもので、再ネゴシエーション拡張が追加されたことによって、ハンドシェイクメッセージの構造が変化したため、それに合わせて更新が必要となりました。これにより、新しい実装が既存のプロトコルバージョンや暗号スイートと正しく連携することを確認しています。

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

このコミットにおける主要なコード変更は、以下のファイルに集中しています。

  • src/pkg/crypto/tls/common.go: 新しい定数の定義。
  • src/pkg/crypto/tls/handshake_client.go: クライアントHelloメッセージの構築ロジックの変更。
  • src/pkg/crypto/tls/handshake_messages.go: TLSハンドシェイクメッセージ構造体(clientHelloMsg, serverHelloMsg)の変更と、それらのマーシャリング/アンマーシャリングロジックの変更。
  • src/pkg/crypto/tls/handshake_server.go: サーバーHelloメッセージの構築ロジックの変更。
  • src/pkg/crypto/tls/handshake_server_test.go: テストケースの修正。
  • src/pkg/crypto/tls/testdata/: 多数のテストデータファイルの更新。

具体的な変更行数は以下の通りです。

--- a/src/pkg/crypto/tls/common.go
+++ b/src/pkg/crypto/tls/common.go
@@ -64,7 +64,7 @@ const (
 )
 
 // TLS extension numbers
-var (
+const (
 	extensionServerName          uint16 = 0
 	extensionStatusRequest       uint16 = 5
 	extensionSupportedCurves     uint16 = 10
@@ -72,11 +72,17 @@ var (
 	extensionSignatureAlgorithms uint16 = 13
 	extensionSessionTicket       uint16 = 35
 	extensionNextProtoNeg        uint16 = 13172 // not IANA assigned
+	extensionRenegotiationInfo   uint16 = 0xff01
+)
+
+// TLS signaling cipher suite values
+const (
+	scsvRenegotiation uint16 = 0x00ff
 )
 
 // TLS Elliptic Curves
 // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
-var (
+const (
 	curveP256 uint16 = 23
 	curveP384 uint16 = 24
 	curveP521 uint16 = 25
@@ -84,7 +90,7 @@ var (
 
 // TLS Elliptic Curve Point Formats
 // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
-var (
+const (
 	pointFormatUncompressed uint8 = 0
 )
--- a/src/pkg/crypto/tls/handshake_client.go
+++ b/src/pkg/crypto/tls/handshake_client.go
@@ -22,14 +22,15 @@ func (c *Conn) clientHandshake() error {
 	}
 
 	hello := &clientHelloMsg{
-		vers:               c.config.maxVersion(),
-		compressionMethods: []uint8{compressionNone},
-		random:             make([]byte, 32),
-		ocspStapling:       true,
-		serverName:         c.config.ServerName,
-		supportedCurves:    []uint16{curveP256, curveP384, curveP521},
-		supportedPoints:    []uint8{pointFormatUncompressed},
-		nextProtoNeg:       len(c.config.NextProtos) > 0,
+		vers:                c.config.maxVersion(),
+		compressionMethods:  []uint8{compressionNone},
+		random:              make([]byte, 32),
+		ocspStapling:        true,
+		serverName:          c.config.ServerName,
+		supportedCurves:     []uint16{curveP256, curveP384, curveP521},
+		supportedPoints:     []uint8{pointFormatUncompressed},
+		nextProtoNeg:        len(c.config.NextProtos) > 0,
+		secureRenegotiation: true,
 	}
 
 	possibleCipherSuites := c.config.cipherSuites()
--- a/src/pkg/crypto/tls/handshake_messages.go
+++ b/src/pkg/crypto/tls/handshake_messages.go
@@ -7,20 +7,21 @@ package tls
 import "bytes"
 
 type clientHelloMsg struct {
-	raw                []byte
-	vers               uint16
-	random             []byte
-	sessionId          []byte
-	cipherSuites       []uint16
-	compressionMethods []uint8
-	nextProtoNeg       bool
-	serverName         string
-	ocspStapling       bool
-	supportedCurves    []uint16
-	supportedPoints    []uint8
-	ticketSupported    bool
-	sessionTicket      []uint8
-	signatureAndHashes []signatureAndHash
+	raw                 []byte
+	vers                uint16
+	random              []byte
+	sessionId           []byte
+	cipherSuites        []uint16
+	compressionMethods  []uint8
+	nextProtoNeg        bool
+	serverName          string
+	ocspStapling        bool
+	supportedCurves     []uint16
+	supportedPoints     []uint8
+	ticketSupported     bool
+	sessionTicket       []uint8
+	signatureAndHashes  []signatureAndHash
+	secureRenegotiation bool
 }
 
 func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -42,7 +43,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
 		bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
 		m.ticketSupported == m1.ticketSupported &&
 		bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
-		eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes)
+		eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) &&
+		m.secureRenegotiation == m1.secureRenegotiation
 }
 
 func (m *clientHelloMsg) marshal() []byte {
@@ -80,6 +82,10 @@ func (m *clientHelloMsg) marshal() []byte {
 		extensionsLength += 2 + 2*len(m.signatureAndHashes)
 		numExtensions++
 	}
+	if m.secureRenegotiation {
+		extensionsLength += 1
+		numExtensions++
+	}
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
 		length += 2 + extensionsLength
@@ -114,13 +120,13 @@ func (m *clientHelloMsg) marshal() []byte {
 	}
 	if m.nextProtoNeg {
 		z[0] = byte(extensionNextProtoNeg >> 8)
-		z[1] = byte(extensionNextProtoNeg)
+		z[1] = byte(extensionNextProtoNeg & 0xff)
 		// The length is always 0
 		z = z[4:]
 	}
 	if len(m.serverName) > 0 {
 		z[0] = byte(extensionServerName >> 8)
-		z[1] = byte(extensionServerName)
+		z[1] = byte(extensionServerName & 0xff)
 		l := len(m.serverName) + 5
 		z[2] = byte(l >> 8)
 		z[3] = byte(l)
@@ -224,6 +230,13 @@ func (m *clientHelloMsg) marshal() []byte {
 		z = z[2:]
 	}
 	}
+	if m.secureRenegotiation {
+		z[0] = byte(extensionRenegotiationInfo >> 8)
+		z[1] = byte(extensionRenegotiationInfo & 0xff)
+		z[2] = 0
+		z[3] = 1
+		z = z[5:]
+	}
 
 	m.raw = x
 
@@ -256,6 +269,9 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
 	m.cipherSuites = make([]uint16, numCipherSuites)
 	for i := 0; i < numCipherSuites; i++ {
 		m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
+		if m.cipherSuites[i] == scsvRenegotiation {
+			m.secureRenegotiation = true
+		}
 	}
 	data = data[2+cipherSuiteLen:]
 	if len(data) < 1 {
@@ -379,6 +395,11 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
 			m.signatureAndHashes[i].signature = d[1]
 			d = d[2:]
 		}
+		case extensionRenegotiationInfo + 1:
+			if length != 1 || data[0] != 0 {
+				return false
+			}
+			m.secureRenegotiation = true
 		}
 		data = data[length:]
 	}
@@ -387,16 +408,17 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
 }
 
 type serverHelloMsg struct {
-	raw               []byte
-	vers              uint16
-	random            []byte
-	sessionId         []byte
-	cipherSuite       uint16
-	compressionMethod uint8
-	nextProtoNeg      bool
-	nextProtos        []string
-	ocspStapling      bool
-	ticketSupported   bool
+	raw                 []byte
+	vers                uint16
+	random              []byte
+	sessionId           []byte
+	cipherSuite         uint16
+	compressionMethod   uint8
+	nextProtoNeg        bool
+	nextProtos          []string
+	ocspStapling        bool
+	ticketSupported     bool
+	secureRenegotiation bool
 }
 
 func (m *serverHelloMsg) equal(i interface{}) bool {
@@ -414,7 +436,8 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
 		m.nextProtoNeg == m1.nextProtoNeg &&
 		eqStrings(m.nextProtos, m1.nextProtos) &&
 		m.ocspStapling == m1.ocspStapling &&
-		m.ticketSupported == m1.ticketSupported
+		m.ticketSupported == m1.ticketSupported &&
+		m.secureRenegotiation == m1.secureRenegotiation
 }
 
 func (m *serverHelloMsg) marshal() []byte {
@@ -441,6 +464,10 @@ func (m *serverHelloMsg) marshal() []byte {
 	if m.ticketSupported {
 		numExtensions++
 	}
+	if m.secureRenegotiation {
+		extensionsLength += 1
+		numExtensions++
+	}
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
 		length += 2 + extensionsLength
@@ -469,7 +496,7 @@ func (m *serverHelloMsg) marshal() []byte {
 	}
 	if m.nextProtoNeg {
 		z[0] = byte(extensionNextProtoNeg >> 8)
-		z[1] = byte(extensionNextProtoNeg)
+		z[1] = byte(extensionNextProtoNeg & 0xff)
 		z[2] = byte(nextProtoLen >> 8)
 		z[3] = byte(nextProtoLen)
 		z = z[4:]
@@ -494,6 +521,13 @@ func (m *serverHelloMsg) marshal() []byte {
 		z[1] = byte(extensionSessionTicket)
 		z = z[4:]
 	}
+	if m.secureRenegotiation {
+		z[0] = byte(extensionRenegotiationInfo >> 8)
+		z[1] = byte(extensionRenegotiationInfo & 0xff)
+		z[2] = 0
+		z[3] = 1
+		z = z[5:]
+	}
 
 	m.raw = x
 
@@ -573,6 +607,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
 			return false
 		}
 		m.ticketSupported = true
+		case extensionRenegotiationInfo:
+			if length != 1 || data[0] != 0 {
+				return false
+			}
+			m.secureRenegotiation = true
 		}
 		data = data[length:]
 	}
--- a/src/pkg/crypto/tls/handshake_server.go
+++ b/src/pkg/crypto/tls/handshake_server.go
@@ -152,6 +152,7 @@ Curves:
 	hs.hello.random[1] = byte(t >> 16)
 	hs.hello.random[2] = byte(t >> 8)
 	hs.hello.random[3] = byte(t)
+	hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation
 	_, err = io.ReadFull(config.rand(), hs.hello.random[4:])
 	if err != nil {
 		return false, c.sendAlert(alertInternalError)

コアとなるコードの解説

common.go

  • extensionRenegotiationInfo (0xff01): これは、TLS拡張のタイプを識別するための定数です。RFC 5746で定義されている再ネゴシエーション情報拡張のIANA割り当て値(0xff01)に対応します。
  • scsvRenegotiation (0x00ff): これは、TLSハンドシェイクのClientHelloメッセージで、クライアントがセキュアな再ネゴシエーションをサポートしていることを示すために使用される特別なシグナリング暗号スイート値(SCSV)です。RFC 5746で定義されています。
  • varからconstへの変更: 既存のTLS拡張番号、楕円曲線、楕円曲線点形式の定義がvarからconstに変更されました。これは、これらの値が実行時に変更されない定数であることをより明確にするための、コードのクリーンアップとベストプラクティスへの準拠です。

handshake_client.go

  • hello.secureRenegotiation = true: クライアントがclientHelloMsgを構築する際に、secureRenegotiationフィールドを常にtrueに設定するように変更されました。これは、GoクライアントがRFC 5746に準拠したセキュアな再ネゴシエーションを常に試みることを意味します。これにより、クライアントはサーバーに対して、自身が再ネゴシエーション攻撃に対する防御メカニズムをサポートしていることを明示的に伝えます。

handshake_messages.go

このファイルは、TLSハンドシェイクメッセージの構造と、それらをバイト列に変換(マーシャリング)したり、バイト列から構造体に変換(アンマーシャリング)したりするロジックを定義しています。

  • clientHelloMsg構造体:

    • secureRenegotiation boolフィールドが追加されました。これは、クライアントがセキュアな再ネゴシエーションをサポートしているかどうかを示すフラグです。
    • equal()メソッド: secureRenegotiationフィールドの比較が追加され、メッセージの等価性チェックが正しく行われるようになりました。
    • marshal()メソッド:
      • secureRenegotiationtrueの場合、extensionRenegotiationInfo拡張がClientHelloメッセージの拡張セクションに追加されるロジックが追加されました。この拡張は、RFC 5746の仕様に従い、長さが1バイトで、その値は0です。
      • 既存の拡張(extensionNextProtoNeg, extensionServerNameなど)のエンコーディングにおいて、バイト値をマスクする& 0xffが追加されました。これは、byte()キャストが上位ビットを切り捨てることを明示し、コードの堅牢性を高めるためのものです。
    • unmarshal()メソッド:
      • 受信したClientHelloメッセージの暗号スイートリストを解析する際に、scsvRenegotiation (0x00ff) が見つかった場合、secureRenegotiationフィールドがtrueに設定されます。これは、サーバーがクライアントからSCSVを受け取った場合に、クライアントがセキュアな再ネゴシエーションをサポートしていると認識するためのものです。
      • 受信したClientHelloメッセージの拡張セクションを解析する際に、extensionRenegotiationInfo拡張が見つかり、その形式がRFC 5746の仕様(長さが1で値が0)に準拠している場合、secureRenegotiationフィールドがtrueに設定されます。
  • serverHelloMsg構造体:

    • secureRenegotiation boolフィールドが追加されました。これは、サーバーがセキュアな再ネゴシエーションをサポートしているかどうかを示すフラグです。
    • equal()メソッド: secureRenegotiationフィールドの比較が追加されました。
    • marshal()メソッド: secureRenegotiationtrueの場合、extensionRenegotiationInfo拡張がServerHelloメッセージの拡張セクションに追加されるロジックが追加されました。
    • unmarshal()メソッド: 受信したServerHelloメッセージの拡張セクションを解析する際に、extensionRenegotiationInfo拡張が見つかり、その形式がRFC 5746の仕様に準拠している場合、secureRenegotiationフィールドがtrueに設定されます。

handshake_server.go

  • hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation: サーバーがserverHelloMsgを構築する際に、クライアントから受け取ったclientHelloMsgsecureRenegotiationフィールドの値を、自身のserverHelloMsgsecureRenegotiationフィールドにコピーするように変更されました。これにより、サーバーはクライアントがセキュアな再ネゴシエーションをサポートしていることを認識し、それに応じて応答します。

handshake_server_test.go

  • TestCipherSuiteCertPreferance関数のリネームとCipherSuitesの変更: テスト関数の名前がTestCipherSuiteCertPreferenceECDSAに変更され、使用されるCipherSuitesもTLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHAからTLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHAに変更されています。これは、テストの意図をより明確にし、より堅牢なテストケースを提供するためのものです。

testdata/

  • 多数のテストデータファイルが更新されています。これらのファイルは、TLSハンドシェイクの実際のバイトストリームを記録したもので、新しい再ネゴシエーション拡張がハンドシェイクメッセージに追加されたため、その変更を反映するために更新されました。これにより、GoのTLS実装がRFC 5746に準拠したハンドシェイクを正しく生成および解析できることが保証されます。

これらの変更により、Goのcrypto/tlsパッケージは、TLS再ネゴシエーション攻撃に対する防御メカニズムであるRFC 5746をサポートし、より安全なTLS通信を提供できるようになりました。

関連リンク

参考にした情報源リンク