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

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

このコミットは、Go言語のcrypto/tlsパッケージにTLS 1.1のサポートを追加するものです。TLS 1.1は、TLS 1.0と比較して特にCBC (Cipher Block Chaining) モードにおけるInitialization Vector (IV) の扱いが変更されており、これによりセキュリティが向上しています。この変更は、TLS 1.2のサポートを可能にするための前提条件でもあります。

コミット

commit 2112fed7437c73d76fe514a0ff5082d56cb69e6b
Author: Adam Langley <agl@golang.org>
Date:   Tue Jun 4 20:02:22 2013 -0400

    crypto/tls: support TLS 1.1.
    
    The significant change between TLS 1.0 and 1.1 is the addition of an explicit IV in the case of CBC encrypted records. Support for TLS 1.1 is needed in order to support TLS 1.2.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/7880043

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

https://github.com/golang/go/commit/2112fed7437c73d76fe514a0ff5082d56cb69e6b

元コミット内容

crypto/tlsパッケージにTLS 1.1のサポートを追加します。TLS 1.0と1.1の間の重要な変更点は、CBC暗号化レコードにおける明示的なIVの追加です。TLS 1.2をサポートするためには、TLS 1.1のサポートが必要です。

変更の背景

このコミットの主な背景は、TLS 1.0に存在するセキュリティ上の脆弱性、特にBEAST (Browser Exploit Against SSL/TLS) 攻撃への対策と、将来的なTLS 1.2サポートへの道を開くことです。

TLS 1.0では、CBCモードで暗号化されたレコードのInitialization Vector (IV) が、前のレコードの最後の暗号文ブロックから導出されていました。この「暗黙的なIV」の生成方法は、攻撃者がIVを予測可能にしてしまい、特定の条件下で平文の一部を推測できる可能性がありました。BEAST攻撃は、このIVの予測可能性と、特定のパディングスキームを悪用して、HTTPクッキーなどの機密情報を復元するものでした。

TLS 1.1では、この脆弱性に対処するため、各レコードに対して新しい予測不可能なIVを生成し、それを暗号文の前に含めて送信する「明示的なIV」の概念が導入されました。これにより、IVのランダム性が保証され、BEASTのようなCBC関連の攻撃に対する耐性が向上しました。

Go言語のcrypto/tlsパッケージが最新のセキュリティ標準に準拠し、より安全な通信を提供するためには、このTLS 1.1のサポートが不可欠でした。また、TLS 1.2はTLS 1.1をベースにしているため、TLS 1.1のサポートはTLS 1.2を実装するための前提条件となります。

前提知識の解説

TLS (Transport Layer Security)

TLSは、インターネット上で安全な通信を行うための暗号化プロトコルです。ウェブブラウジング(HTTPS)、電子メール、VoIPなど、様々なアプリケーションで利用されています。TLSは、クライアントとサーバー間でデータの機密性、完全性、認証を保証します。

CBC (Cipher Block Chaining) モード

CBCは、ブロック暗号の運用モードの一つです。各ブロックの暗号化は、前のブロックの暗号文と現在の平文ブロックをXORすることで行われます。これにより、同じ平文ブロックが繰り返されても、異なる暗号文ブロックが生成されるため、パターン攻撃を防ぐことができます。 CBCモードでは、最初のブロックを暗号化するためにInitialization Vector (IV) と呼ばれるランダムな値が必要です。後続のブロックでは、前のブロックの暗号文がIVとして機能します。

Initialization Vector (IV)

IVは、暗号化プロセスを開始するために使用されるランダムなビット列です。CBCモードのようなブロック暗号モードでは、同じ鍵で同じ平文を複数回暗号化しても、異なる暗号文が生成されるようにするためにIVが使用されます。

  • 暗黙的なIV (Implicit IV): TLS 1.0以前では、CBCモードのIVは前のレコードの最後の暗号文ブロックから導出されていました。これは、攻撃者がIVを予測できる可能性があり、セキュリティ上の問題を引き起こしました。
  • 明示的なIV (Explicit IV): TLS 1.1以降では、各レコードごとに新しいランダムなIVが生成され、暗号文の前に平文で送信されます。これにより、IVの予測可能性が排除され、セキュリティが向上します。

BEAST (Browser Exploit Against SSL/TLS) 攻撃

BEAST攻撃は、TLS 1.0およびSSL 3.0のCBCモードにおける暗黙的なIVの脆弱性を悪用したサイドチャネル攻撃です。攻撃者は、既知の平文と暗号文のペアを利用し、IVの予測可能性とパディングの特性を組み合わせて、暗号化された通信からクッキーなどの機密情報を推測します。この攻撃は、特にブラウザとウェブサーバー間のHTTPS通信で有効でした。

技術的詳細

このコミットは、Go言語のcrypto/tlsパッケージにおいて、TLS 1.1の主要な変更点である「明示的なIV」のサポートを実装しています。

TLS 1.0とTLS 1.1の最も重要な違いは、CBCモードにおけるIVの扱いです。

  • TLS 1.0: IVは前のレコードの最後の暗号文ブロックから導出される(暗黙的IV)。これにより、IVが予測可能になり、BEAST攻撃のような脆弱性が生じました。
  • TLS 1.1: 各レコードごとに新しいランダムなIVが生成され、暗号文の前に平文で送信される(明示的IV)。これにより、IVの予測可能性が排除され、CBCモードのセキュリティが大幅に向上しました。

このコミットでは、以下の技術的な変更が行われています。

  1. バージョン定数の追加と更新:

    • src/pkg/crypto/tls/common.goにおいて、VersionTLS11 (0x0302) が新しいプロトコルバージョンとして追加されました。
    • minVersionmaxVersionの定数が更新され、maxVersionVersionTLS11を指すようになりました。これにより、GoのTLS実装がデフォルトでTLS 1.1をサポートするようになります。
  2. CBCモードインターフェースの拡張:

    • src/pkg/crypto/cipher/cbc.goに、cbcEncryptercbcDecrypter構造体にSetIV([]byte)メソッドが追加されました。これは、CBCモードの暗号化器/復号化器が外部からIVを設定できるようにするためのものです。
    • src/pkg/crypto/tls/conn.gocbcModeインターフェースが定義され、cipher.BlockModeSetIV([]byte)メソッドを持つ型がこのインターフェースを満たすようにしました。これにより、TLSレイヤーがCBC暗号化器のIVを動的に設定できるようになります。
  3. レコード暗号化/復号化ロジックの変更:

    • src/pkg/crypto/tls/conn.godecrypt関数とencrypt関数が、TLS 1.1の明示的IVを処理するように修正されました。
    • decrypt関数では、explicitIVLenという変数が導入され、TLS 1.1以上のバージョンではブロックサイズ分のIVがペイロードの先頭から読み取られるようになりました。このIVはcbcMode.SetIVを使って設定され、実際の暗号文はIVの後に続くデータとして処理されます。
    • encrypt関数では、TLS 1.1以上のバージョンでCBCモードが使用される場合、暗号化前にランダムなIVが生成され、レコードヘッダーと実際のペイロードの間に挿入されるようになりました。このIVもcbcMode.SetIVを使って設定されます。
  4. ハンドシェイクプロセスのバージョンネゴシエーションの更新:

    • src/pkg/crypto/tls/common.go(*Config).minVersion(), (*Config).maxVersion(), (*Config).mutualVersion()メソッドが追加されました。これにより、TLS設定に基づいて許容される最小/最大バージョンと、ピアとの間で合意されるバージョンをより柔軟に決定できるようになりました。
    • src/pkg/crypto/tls/handshake_client.gosrc/pkg/crypto/tls/handshake_server.goにおいて、クライアントとサーバーのハンドシェイク時に、これらの新しいバージョンネゴシエーションロジックが使用されるようになりました。これにより、TLS 1.1のネゴシエーションが可能になります。
  5. テストケースの追加:

    • src/pkg/crypto/tls/handshake_client_test.gosrc/pkg/crypto/tls/handshake_server_test.goに、TLS 1.1のハンドシェイクをテストするための新しいテストケースが追加されました。これにより、TLS 1.1のサポートが正しく機能することを確認できます。

これらの変更により、GoのTLS実装はTLS 1.1のセキュリティ強化に対応し、より堅牢な通信を提供できるようになりました。

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

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

  • doc/go1.2.txt: Go 1.2のリリースノートにTLS 1.1サポートが追加されたことを記載。
  • src/pkg/crypto/cipher/cbc.go: CBC暗号化器/復号化器にIVを設定するメソッドを追加。
  • src/pkg/crypto/tls/cipher_suites.go: MAC計算関数のシグネチャ変更(ヘッダーとデータが分離)。
  • src/pkg/crypto/tls/common.go: TLSバージョン定数(TLS 1.1)の追加と、バージョンネゴシエーションロジックの改善。
  • src/pkg/crypto/tls/conn.go: TLSレコードの暗号化/復号化ロジックをTLS 1.1の明示的IVに対応させる変更。
  • src/pkg/crypto/tls/handshake_client.go: クライアントハンドシェイクにおけるバージョンネゴシエーションの更新。
  • src/pkg/crypto/tls/handshake_client_test.go: TLS 1.1クライアントハンドシェイクのテストケース追加。
  • src/pkg/crypto/tls/handshake_server.go: サーバーハンドシェイクにおけるバージョンネゴシエーションの更新。
  • src/pkg/crypto/tls/handshake_server_test.go: TLS 1.1サーバーハンドシェイクのテストケース追加。
  • src/pkg/crypto/tls/key_agreement.go: 鍵合意プロセスのバージョンチェックの更新。
  • src/pkg/crypto/tls/prf.go: PRF (Pseudo-Random Function) のバージョンチェックの更新。
  • src/pkg/crypto/tls/prf_test.go: PRFテストのバージョン定数更新。

特に、src/pkg/crypto/tls/conn.goにおけるdecryptおよびencrypt関数の変更と、src/pkg/crypto/cipher/cbc.goへのSetIVメソッドの追加が、明示的IVサポートの核心部分です。

コアとなるコードの解説

src/pkg/crypto/cipher/cbc.go

func (x *cbcEncrypter) SetIV(iv []byte) {
	if len(iv) != len(x.iv) {
		panic("cipher: incorrect length IV")
	}
	copy(x.iv, iv)
}

func (x *cbcDecrypter) SetIV(iv []byte) {
	if len(iv) != len(x.iv) {
		panic("cipher: incorrect length IV")
	}
	copy(x.iv, iv)
}

この変更は、CBCモードの暗号化器と復号化器が、外部からInitialization Vector (IV) を設定できるようにするためのものです。TLS 1.1では、各レコードの暗号化/復号化に新しいランダムなIVが使用されるため、この機能が必要となります。SetIVメソッドは、提供されたIVを内部のIVバッファにコピーします。

src/pkg/crypto/tls/common.go

const (
	VersionSSL30 = 0x0300
	VersionTLS10 = 0x0301
	VersionTLS11 = 0x0302
)

const (
	// ...
	minVersion = VersionSSL30
	maxVersion = VersionTLS11
)

// ...

func (c *Config) mutualVersion(vers uint16) (uint16, bool) {
	minVersion := c.minVersion()
	maxVersion := c.maxVersion()

	if vers < minVersion {
		return 0, false
	}
	if vers > maxVersion {
		vers = maxVersion
	}
	return vers, true
}

VersionTLS11という新しい定数が追加され、maxVersionVersionTLS11に更新されました。これにより、GoのTLS実装がデフォルトでTLS 1.1をサポートするようになります。 mutualVersion関数は、クライアントとサーバー間で合意可能なTLSプロトコルバージョンを決定するためのロジックをカプセル化しています。これは、Config構造体のMinVersionMaxVersion設定を考慮し、ピアが提示したバージョンと自身のサポート範囲を比較して、最適なバージョンを選択します。

src/pkg/crypto/tls/conn.go

type cbcMode interface {
	cipher.BlockMode
	SetIV([]byte)
}

func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) {
	// ...
	explicitIVLen := 0
	// ...
	case cbcMode:
		blockSize := c.BlockSize()
		if hc.version >= VersionTLS11 {
			explicitIVLen = blockSize
		}

		if explicitIVLen > 0 {
			c.SetIV(payload[:explicitIVLen])
			payload = payload[explicitIVLen:]
		}
		c.CryptBlocks(payload, payload)
	// ...
	return true, recordHeaderLen + explicitIVLen, 0
}

func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
	// ...
	case cbcMode:
		blockSize := c.BlockSize()
		if explicitIVLen > 0 {
			c.SetIV(payload[:explicitIVLen])
			payload = payload[explicitIVLen:]
		}
		// ...
		c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix)
		c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock)
	// ...
}

func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
	// ...
	explicitIVLen := 0
	var cbc cbcMode
	if c.out.version >= VersionTLS11 {
		if cbc, ok = c.out.cipher.(cbcMode); ok {
			explicitIVLen = cbc.BlockSize()
		}
	}
	b.resize(recordHeaderLen + explicitIVLen + m)
	// ...
	if explicitIVLen > 0 {
		explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
		if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
			break
		}
	}
	copy(b.data[recordHeaderLen+explicitIVLen:], data)
	c.out.encrypt(b, explicitIVLen)
	// ...
}

cbcModeインターフェースが定義され、cipher.BlockModeに加えてSetIVメソッドを持つ型を扱うことができるようになりました。 decrypt関数では、TLS 1.1以上のバージョンでCBCモードが使用される場合、レコードの先頭からブロックサイズ分のデータが明示的IVとして読み取られ、cbcMode.SetIVを使って設定されます。これにより、復号化器は正しいIVで動作します。 encrypt関数とwriteRecord関数では、TLS 1.1以上のバージョンでCBCモードが使用される場合、ランダムなIVが生成され、レコードヘッダーと実際の暗号化データの間(recordHeaderLenとペイロードの間)に挿入されます。このIVはcbcMode.SetIVを使って暗号化器に渡され、その後のデータが暗号化されます。

これらの変更により、GoのTLS実装はTLS 1.1の明示的IVの要件を満たし、BEAST攻撃のような脆弱性から保護されるようになりました。

関連リンク

参考にした情報源リンク