[インデックス 10449] ファイルの概要
このコミットは、Go言語の実験的なSSHパッケージ(exp/ssh
)において、RFC 4253、RFC 4344、RFC 4345で定義されている主要な暗号スイートのサポートを追加するものです。これにより、SSH接続における暗号化の選択肢が大幅に拡張され、より現代的でセキュアな暗号アルゴリズム(特にCTRモードのAESと改良されたArcfour)が利用可能になります。
コミット
commit 0e60804b4a65559613ceae03b8a61b959d0a1cba
Author: John Beisley <huin@google.com>
Date: Fri Nov 18 12:56:57 2011 -0500
exp/ssh: Add support for (most) of the ciphers from RFC4253, RFC4344 and RFC4345.
R=dave, agl, taruti, rsc, r
CC=golang-dev
https://golang.org/cl/5342057
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0e60804b4a65559613ceae03b8a61b959d0a1cba
元コミット内容
exp/ssh: Add support for (most) of the ciphers from RFC4253, RFC4344 and RFC4345.
変更の背景
このコミットが行われた2011年当時、Go言語の実験的なSSHパッケージ(exp/ssh
)は、限られた暗号アルゴリズムしかサポートしていませんでした。特に、SSHプロトコルにおいて広く利用され、セキュリティが強化された暗号モード(例えばAESのCTRモードや改良されたArcfour)への対応が不足していました。
SSHプロトコルは、クライアントとサーバー間の安全な通信を確立するために、鍵交換、認証、そしてデータの暗号化と完全性保護の各フェーズで様々なアルゴリズムを使用します。暗号化アルゴリズムは、通信されるデータの機密性を保証する上で極めて重要です。
RFC 4253はSSHトランスポート層プロトコルの基本を定義しており、暗号化アルゴリズムのネゴシエーションメカニズムを含んでいます。RFC 4344とRFC 4345は、それぞれAESのCTRモードと改良されたArcfour(RC4)モードをSSHに導入し、既存の暗号アルゴリズムに対するセキュリティとパフォーマンスの改善を提供しました。
このコミットの背景には、GoのSSH実装がこれらの標準的な、かつ推奨される暗号アルゴリズムをサポートすることで、相互運用性を高め、より堅牢なセキュリティを提供する必要があったと考えられます。特に、AES-CTRはブロック暗号をストリーム暗号のように扱うことができ、並列処理に適しているため、パフォーマンス面でも有利です。また、Arcfourの改良版は、初期の脆弱性を回避するためのスキップ処理を導入しています。
前提知識の解説
SSH (Secure Shell) プロトコル
SSHは、ネットワークを介して安全にコンピュータを操作するためのプロトコルです。主にリモートログインやファイル転送(SCP, SFTP)に利用されます。SSHは、公開鍵暗号方式と共通鍵暗号方式を組み合わせて、認証、データの機密性、完全性、および認証を提供します。
SSHの接続確立プロセスは、大きく以下のフェーズに分けられます。
- トランスポート層プロトコル (Transport Layer Protocol): RFC 4253で定義されており、サーバー認証、機密性、データの完全性を提供する暗号化されたトンネルを確立します。このフェーズで鍵交換アルゴリズム、サーバーホスト鍵アルゴリズム、暗号化アルゴリズム(Cipher)、メッセージ認証コード(MAC)アルゴリズム、圧縮アルゴリズムがネゴシエートされます。
- ユーザー認証プロトコル (User Authentication Protocol): RFC 4252で定義されており、クライアントがサーバーに対して自身を認証します。パスワード認証、公開鍵認証、ホストベース認証などがあります。
- 接続プロトコル (Connection Protocol): RFC 4254で定義されており、確立されたセキュアなチャネル上で複数の論理チャネル(セッション、X11転送、ポート転送など)を多重化します。
暗号化アルゴリズム (Cipher)
SSHにおける暗号化アルゴリズムは、トランスポート層で交換されるデータを暗号化・復号化するために使用されます。
- ブロック暗号: データを固定長のブロックに分割して処理します。例: AES (Advanced Encryption Standard)。
- ストリーム暗号: データをビットまたはバイト単位で連続的に処理します。例: RC4 (Rivest Cipher 4)。
暗号モード
ブロック暗号は、そのままでは単一のブロックしか暗号化できません。連続するデータを暗号化するためには、特定の「モード」と組み合わせて使用されます。
- CTR (Counter) モード: ブロック暗号をストリーム暗号のように動作させるモードです。カウンタ値を暗号化し、その結果を平文とXORすることで暗号文を生成します。各ブロックの暗号化が独立しているため、並列処理が可能で、高速な処理が期待できます。また、ランダムアクセスが可能で、パディングが不要という利点があります。
- CBC (Cipher Block Chaining) モード: 各ブロックの暗号化が前のブロックの暗号文に依存するモードです。
RFC 4253: The Secure Shell (SSH) Transport Layer Protocol
SSHのトランスポート層プロトコルを定義する基本的なRFCです。鍵交換、暗号化、メッセージ認証、圧縮などのネゴシエーションと適用方法について記述されています。このRFCは、SSH接続のセキュリティ基盤を形成します。
RFC 4344: The Secure Shell (SSH) Transport Layer Protocol Cryptographic Message Syntax (CMS) for AES-CTR
このRFCは、SSHトランスポート層プロトコルにおけるAESブロック暗号のCTRモードの使用を定義しています。具体的には、aes128-ctr
, aes192-ctr
, aes256-ctr
といった暗号スイートが導入され、それぞれの鍵長(128, 192, 256ビット)に対応します。CTRモードは、その特性からSSHのようなストリームベースのプロトコルに適しています。
RFC 4345: Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol
このRFCは、ストリーム暗号であるArcfour(RC4)の改良版をSSHに導入しています。従来のRC4には初期の出力ストリームに偏りがあるという既知の脆弱性がありましたが、このRFCで定義されるarcfour128
とarcfour256
は、初期の1536バイトのキーストリームを破棄(スキップ)することで、この脆弱性を緩和しています。これにより、より安全にArcfourを使用できるようになります。
crypto/aes
および crypto/cipher
パッケージ (Go言語)
crypto/aes
: AESブロック暗号の実装を提供します。aes.NewCipher
関数でAES暗号器のインスタンスを作成できます。crypto/cipher
: 共通の暗号インターフェース(cipher.Block
,cipher.Stream
など)と、様々な暗号モード(CTR, CBCなど)の実装を提供します。cipher.NewCTR
関数は、ブロック暗号と初期ベクトル(IV)からCTRモードのストリーム暗号器を作成します。
crypto/rc4
パッケージ (Go言語)
crypto/rc4
: RC4ストリーム暗号の実装を提供します。rc4.NewCipher
関数でRC4暗号器のインスタンスを作成できます。
技術的詳細
このコミットの主要な技術的変更点は、GoのSSHパッケージに複数の新しい暗号アルゴリズムを統合し、それらを柔軟に設定できるようにしたことです。
-
cipher.go
の新規追加:noneCipher
構造体: 暗号化を行わない(コピーのみ)cipher.Stream
の実装。鍵交換前の初期状態や、暗号化が不要な場合に利用されます。newAESCTR
関数: AESブロック暗号とCTRモードを組み合わせてcipher.Stream
を生成します。crypto/aes
とcrypto/cipher
パッケージを利用します。newRC4
関数: RC4ストリーム暗号からcipher.Stream
を生成します。crypto/rc4
パッケージを利用します。cipherMode
構造体: 各暗号アルゴリズムのメタデータ(鍵サイズkeySize
、IVサイズivSize
、スキップバイト数skip
、およびストリーム暗号器を生成する関数createFn
)をカプセル化します。cipherMode.createCipher
メソッド:cipherMode
の情報に基づいて、実際のcipher.Stream
インスタンスを生成します。特に、Arcfourのスキップ処理(RFC 4345で定義される1536バイトの初期キーストリーム破棄)を実装しています。DefaultCipherOrder
変数: OpenSSHのデフォルトクライアントの優先順位に基づいた、サポートされる暗号アルゴリズムのデフォルトリストを定義します。これにはaes128-ctr
,aes192-ctr
,aes256-ctr
,arcfour256
,arcfour128
が含まれます。cipherModes
マップ: 各暗号アルゴリズム名とそれに対応するcipherMode
インスタンスをマッピングします。これにより、アルゴリズム名からその特性と生成関数を動的に取得できるようになります。
-
ClientConfig
およびServerConfig
へのCryptoConfig
の追加:common.go
にCryptoConfig
構造体が新設されました。この構造体は、クライアントとサーバーの両方で共通の暗号関連設定を保持します。CryptoConfig
はCiphers
フィールドを持ち、ユーザーが許可する暗号アルゴリズムのリストを文字列スライスで指定できるようにします。このフィールドが指定されない場合、DefaultCipherOrder
が使用されます。ClientConfig
とServerConfig
にCrypto
フィールド(型はCryptoConfig
)が追加され、SSH接続の暗号設定を外部から制御できるようになりました。
-
鍵交換メッセージ (
kexInitMsg
) の更新:client.go
とserver.go
において、鍵交換初期化メッセージ (kexInitMsg
) のCiphersClientServer
とCiphersServerClient
フィールドが、ハードコードされたsupportedCiphers
ではなく、ClientConfig
またはServerConfig
のCryptoConfig
から取得されるようになりました。これにより、ネゴシエートされる暗号アルゴリズムが設定に基づいて動的に決定されます。
-
transport.go
の変更:packetSizeMultiple
定数が導入され、パディングの計算に使用されます。これは、暗号アルゴリズムによってブロックサイズが異なるため、より柔軟なパディング処理を可能にするための変更です。reader
とwriter
の初期化時に、初期の暗号器としてnoneCipher
が設定されるようになりました。これは、鍵交換が完了するまでは暗号化が行われないためです。setupKeys
関数が大幅に修正されました。以前はAESに特化していましたが、新しいcipherModes
マップとcipherMode.createCipher
メソッドを使用して、ネゴシエートされた暗号アルゴリズムに基づいて適切なストリーム暗号器を動的に生成するようになりました。これにより、複数の暗号アルゴリズムに対応できるようになりました。
-
テストの追加 (
cipher_test.go
):TestCipherReversal
は、各暗号モードが正しく暗号化・復号化できることを検証します。データが暗号化され、その後正しく復号化されて元のデータに戻ることを確認します。TestDefaultCiphersExist
は、DefaultCipherOrder
にリストされているすべての暗号アルゴリズムがcipherModes
マップに存在し、正しく定義されていることを確認します。
これらの変更により、GoのSSHパッケージは、より多くの標準的な暗号アルゴリズムをサポートし、ユーザーが暗号設定を柔軟にカスタマイズできるようになり、セキュリティと相互運用性が向上しました。
コアとなるコードの変更箇所
src/pkg/exp/ssh/cipher.go
(新規追加)
package ssh
import (
"crypto/aes"
"crypto/cipher"
"crypto/rc4"
)
// streamDump is used to dump the initial keystream for stream ciphers. It is a
// a write-only buffer, and not intended for reading so do not require a mutex.
var streamDump [512]byte
// noneCipher implements cipher.Stream and provides no encryption. It is used
// by the transport before the first key-exchange.
type noneCipher struct{}
func (c noneCipher) XORKeyStream(dst, src []byte) {
copy(dst, src)
}
func newAESCTR(key, iv []byte) (cipher.Stream, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cipher.NewCTR(c, iv), nil
}
func newRC4(key, iv []byte) (cipher.Stream, error) {
return rc4.NewCipher(key)
}
type cipherMode struct {
keySize int
ivSize int
skip int
createFn func(key, iv []byte) (cipher.Stream, error)
}
func (c *cipherMode) createCipher(key, iv []byte) (cipher.Stream, error) {
if len(key) < c.keySize {
panic("ssh: key length too small for cipher")
}
if len(iv) < c.ivSize {
panic("ssh: iv too small for cipher")
}
stream, err := c.createFn(key[:c.keySize], iv[:c.ivSize])
if err != nil {
return nil, err
}
for remainingToDump := c.skip; remainingToDump > 0; {
dumpThisTime := remainingToDump
if dumpThisTime > len(streamDump) {
dumpThisTime = len(streamDump)
}
stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
remainingToDump -= dumpThisTime
}
return stream, nil
}
// Specifies a default set of ciphers and a preference order. This is based on
// OpenSSH's default client preference order, minus algorithms that are not
// implemented.
var DefaultCipherOrder = []string{
"aes128-ctr", "aes192-ctr", "aes256-ctr",
"arcfour256", "arcfour128",
}
var cipherModes = map[string]*cipherMode{
// Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms
// are defined in the order specified in the RFC.
"aes128-ctr": &cipherMode{16, aes.BlockSize, 0, newAESCTR},
"aes192-ctr": &cipherMode{24, aes.BlockSize, 0, newAESCTR},
"aes256-ctr": &cipherMode{32, aes.BlockSize, 0, newAESCTR},
// Ciphers from RFC4345, which introduces security-improved arcfour ciphers.
// They are defined in the order specified in the RFC.
"arcfour128": &cipherMode{16, 0, 1536, newRC4},
"arcfour256": &cipherMode{32, 0, 1536, newRC4},
}
src/pkg/exp/ssh/common.go
(変更箇所抜粋)
// Cryptographic configuration common to both ServerConfig and ClientConfig.
type CryptoConfig struct {
// The allowed cipher algorithms. If unspecified then DefaultCipherOrder is
// used.
Ciphers []string
}
func (c *CryptoConfig) ciphers() []string {
if c.Ciphers == nil {
return DefaultCipherOrder
}
return c.Ciphers
}
src/pkg/exp/ssh/transport.go
(変更箇所抜粋)
func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) error {
- h := hashFunc.New()
-
- blockSize := 16
- keySize := 16
macKeySize := 20
- iv := make([]byte, blockSize)
- key := make([]byte, keySize)
+ cipherMode := cipherModes[c.cipherAlgo]
+
+ iv := make([]byte, cipherMode.ivSize)
+ key := make([]byte, cipherMode.keySize)
macKey := make([]byte, macKeySize)
+ h := hashFunc.New()
generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h)
generateKeyMaterial(key, d.keyTag, K, H, sessionId, h)
generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h)
c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)}
- aes, err := aes.NewCipher(key)
+
+ cipher, err := cipherMode.createCipher(key, iv)
if err != nil {
return err
}
- c.cipher = cipher.NewCTR(aes, iv)
+
+ c.cipher = cipher
+
return nil
}
コアとなるコードの解説
src/pkg/exp/ssh/cipher.go
このファイルは、SSHプロトコルで使用される様々な暗号アルゴリズムの実装と管理の中心となります。
noneCipher
: 鍵交換が完了するまでの初期状態や、暗号化が不要な場合のプレースホルダーとして機能します。XORKeyStream
メソッドは単にデータをコピーするだけで、暗号化は行いません。newAESCTR
: AESブロック暗号をCTRモードで動作させるためのストリーム暗号器を生成します。aes.NewCipher
でAES暗号器を作成し、cipher.NewCTR
でそれをCTRモードのストリーム暗号器にラップします。newRC4
: RC4ストリーム暗号器を生成します。cipherMode
構造体: 各暗号アルゴリズムの特性を定義します。keySize
: 暗号鍵のバイト長。ivSize
: 初期ベクトル(IV)のバイト長。CTRモードではIVが重要です。skip
: ストリーム暗号(特にArcfour)で初期キーストリームを破棄するバイト数。RFC 4345で定義されるArcfourのセキュリティ強化のための重要な要素です。createFn
: 実際のcipher.Stream
インスタンスを生成するための関数ポインタ。
cipherMode.createCipher
メソッド:cipherMode
の定義に基づいて、実際の暗号器を生成します。特に注目すべきはfor remainingToDump := c.skip; ...
のループです。これはskip
フィールドが0より大きい場合に、生成されたストリーム暗号器の初期キーストリームを指定されたバイト数だけ「ダンプ」(破棄)する処理です。これにより、Arcfourの既知の脆弱性(初期キーストリームの偏り)を回避します。DefaultCipherOrder
: SSHクライアントがサーバーに提示する暗号アルゴリズムの優先順位リストです。OpenSSHの慣例に従い、よりセキュアでパフォーマンスの良いCTRモードのAESが優先され、次に改良されたArcfourが続きます。cipherModes
マップ: 各暗号アルゴリズム名(例: "aes128-ctr")と、それに対応するcipherMode
インスタンスを関連付けます。これにより、SSHプロトコルネゴシエーションで合意されたアルゴリズム名から、そのアルゴリズムの具体的な実装と特性を動的に取得できるようになります。
src/pkg/exp/ssh/common.go
このファイルでは、クライアントとサーバーで共通の暗号設定をカプセル化する CryptoConfig
構造体が定義されています。
CryptoConfig
構造体:Ciphers []string
: ユーザーが明示的に許可する暗号アルゴリズムのリストを指定できます。このリストがnil
の場合、DefaultCipherOrder
が使用されます。これにより、SSH接続の暗号アルゴリズム選択に柔軟性を持たせることができます。
ciphers()
メソッド:CryptoConfig
のCiphers
フィールドが設定されている場合はその値を返し、設定されていない場合はDefaultCipherOrder
を返します。
src/pkg/exp/ssh/transport.go
このファイルは、SSHトランスポート層の低レベルなデータ送受信と暗号化処理を扱います。
setupKeys
関数: 鍵交換が完了した後、実際にデータを暗号化・復号化するための鍵と暗号器を設定する重要な関数です。- 以前のバージョンではAESに特化していましたが、このコミットにより、
cipherModes[c.cipherAlgo]
を使用して、ネゴシエートされた暗号アルゴリズムに対応するcipherMode
を動的に取得するようになりました。 - 取得した
cipherMode
からivSize
とkeySize
を取得し、それに基づいてIVと鍵を生成します。 - 最も重要な変更は、
cipherMode.createCipher(key, iv)
を呼び出して、ネゴシエートされたアルゴリズムに対応するcipher.Stream
インスタンスを生成する点です。これにより、AES-CTRやArcfourなど、様々な暗号アルゴリズムを透過的に利用できるようになります。 - 生成されたストリーム暗号器は
c.cipher
に設定され、以降のデータ送受信で暗号化・復号化に使用されます。
- 以前のバージョンではAESに特化していましたが、このコミットにより、
これらの変更により、GoのSSHパッケージは、より多くの標準的な暗号アルゴリズムをサポートし、ユーザーが暗号設定を柔軟にカスタマイズできるようになり、セキュリティと相互運用性が向上しました。
関連リンク
- RFC 4253: The Secure Shell (SSH) Transport Layer Protocol
- RFC 4344: The Secure Shell (SSH) Transport Layer Protocol Cryptographic Message Syntax (CMS) for AES-CTR
- RFC 4345: Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol
- GoDoc: crypto/aes
- GoDoc: crypto/cipher
- GoDoc: crypto/rc4
参考にした情報源リンク
- 上記のRFCドキュメント
- Go言語の標準ライブラリのドキュメント (
crypto/aes
,crypto/cipher
,crypto/rc4
) - SSHプロトコルに関する一般的な知識
- OpenSSHの暗号アルゴリズムの選択に関する情報 (OpenSSHのmanページや設定ファイルなど)
- CTRモードとArcfour(RC4)の動作原理に関する暗号学の知識
- Go言語のコードベース (
src/pkg/exp/ssh
ディレクトリ内の他のファイル)
[インデックス 10449] ファイルの概要
このコミットは、Go言語の実験的なSSHパッケージ(exp/ssh
)において、RFC 4253、RFC 4344、RFC 4345で定義されている主要な暗号スイートのサポートを追加するものです。これにより、SSH接続における暗号化の選択肢が大幅に拡張され、より現代的でセキュアな暗号アルゴリズム(特にCTRモードのAESと改良されたArcfour)が利用可能になります。
コミット
commit 0e60804b4a65559613ceae03b8a61b959d0a1cba
Author: John Beisley <huin@google.com>
Date: Fri Nov 18 12:56:57 2011 -0500
exp/ssh: Add support for (most) of the ciphers from RFC4253, RFC4344 and RFC4345.
R=dave, agl, taruti, rsc, r
CC=golang-dev
https://golang.org/cl/5342057
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0e60804b4a65559613ceae03b8a61b959d0a1cba
元コミット内容
exp/ssh: Add support for (most) of the ciphers from RFC4253, RFC4344 and RFC4345.
変更の背景
このコミットが行われた2011年当時、Go言語の実験的なSSHパッケージ(exp/ssh
)は、限られた暗号アルゴリズムしかサポートしていませんでした。特に、SSHプロトコルにおいて広く利用され、セキュリティが強化された暗号モード(例えばAESのCTRモードや改良されたArcfour)への対応が不足していました。
SSHプロトコルは、クライアントとサーバー間の安全な通信を確立するために、鍵交換、認証、そしてデータの暗号化と完全性保護の各フェーズで様々なアルゴリズムを使用します。暗号化アルゴリズムは、通信されるデータの機密性を保証する上で極めて重要です。
RFC 4253はSSHトランスポート層プロトコルの基本を定義しており、暗号化アルゴリズムのネゴシエーションメカニズムを含んでいます。RFC 4344とRFC 4345は、それぞれAESのCTRモードと改良されたArcfour(RC4)モードをSSHに導入し、既存の暗号アルゴリズムに対するセキュリティとパフォーマンスの改善を提供しました。
このコミットの背景には、GoのSSH実装がこれらの標準的な、かつ推奨される暗号アルゴリズムをサポートすることで、相互運用性を高め、より堅牢なセキュリティを提供する必要があったと考えられます。特に、AES-CTRはブロック暗号をストリーム暗号のように扱うことができ、並列処理に適しているため、パフォーマンス面でも有利です。また、Arcfourの改良版は、初期の脆弱性を回避するためのスキップ処理を導入しています。
前提知識の解説
SSH (Secure Shell) プロトコル
SSHは、ネットワークを介して安全にコンピュータを操作するためのプロトコルです。主にリモートログインやファイル転送(SCP, SFTP)に利用されます。SSHは、公開鍵暗号方式と共通鍵暗号方式を組み合わせて、認証、データの機密性、完全性、および認証を提供します。
SSHの接続確立プロセスは、大きく以下のフェーズに分けられます。
- トランスポート層プロトコル (Transport Layer Protocol): RFC 4253で定義されており、サーバー認証、機密性、データの完全性を提供する暗号化されたトンネルを確立します。このフェーズで鍵交換アルゴリズム、サーバーホスト鍵アルゴリズム、暗号化アルゴリズム(Cipher)、メッセージ認証コード(MAC)アルゴリズム、圧縮アルゴリズムがネゴシエートされます。
- ユーザー認証プロトコル (User Authentication Protocol): RFC 4252で定義されており、クライアントがサーバーに対して自身を認証します。パスワード認証、公開鍵認証、ホストベース認証などがあります。
- 接続プロトコル (Connection Protocol): RFC 4254で定義されており、確立されたセキュアなチャネル上で複数の論理チャネル(セッション、X11転送、ポート転送など)を多重化します。
暗号化アルゴリズム (Cipher)
SSHにおける暗号化アルゴリズムは、トランスポート層で交換されるデータを暗号化・復号化するために使用されます。
- ブロック暗号: データを固定長のブロックに分割して処理します。例: AES (Advanced Encryption Standard)。
- ストリーム暗号: データをビットまたはバイト単位で連続的に処理します。例: RC4 (Rivest Cipher 4)。
暗号モード
ブロック暗号は、そのままでは単一のブロックしか暗号化できません。連続するデータを暗号化するためには、特定の「モード」と組み合わせて使用されます。
- CTR (Counter) モード: ブロック暗号をストリーム暗号のように動作させるモードです。カウンタ値を暗号化し、その結果を平文とXORすることで暗号文を生成します。各ブロックの暗号化が独立しているため、並列処理が可能で、高速な処理が期待できます。また、ランダムアクセスが可能で、パディングが不要という利点があります。
- CBC (Cipher Block Chaining) モード: 各ブロックの暗号化が前のブロックの暗号文に依存するモードです。
RFC 4253: The Secure Shell (SSH) Transport Layer Protocol
SSHのトランスポート層プロトコルを定義する基本的なRFCです。鍵交換、暗号化、メッセージ認証、圧縮などのネゴシエーションと適用方法について記述されています。このRFCは、SSH接続のセキュリティ基盤を形成します。
RFC 4344: The Secure Shell (SSH) Transport Layer Protocol Cryptographic Message Syntax (CMS) for AES-CTR
このRFCは、SSHトランスポート層プロトコルにおけるAESブロック暗号のCTRモードの使用を定義しています。具体的には、aes128-ctr
, aes192-ctr
, aes256-ctr
といった暗号スイートが導入され、それぞれの鍵長(128, 192, 256ビット)に対応します。CTRモードは、その特性からSSHのようなストリームベースのプロトコルに適しています。
RFC 4345: Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol
このRFCは、ストリーム暗号であるArcfour(RC4)の改良版をSSHに導入しています。従来のRC4には初期の出力ストリームに偏りがあるという既知の脆弱性がありましたが、このRFCで定義されるarcfour128
とarcfour256
は、初期の1536バイトのキーストリームを破棄(スキップ)することで、この脆弱性を緩和しています。これにより、より安全にArcfourを使用できるようになります。
crypto/aes
および crypto/cipher
パッケージ (Go言語)
crypto/aes
: AESブロック暗号の実装を提供します。aes.NewCipher
関数でAES暗号器のインスタンスを作成できます。crypto/cipher
: 共通の暗号インターフェース(cipher.Block
,cipher.Stream
など)と、様々な暗号モード(CTR, CBCなど)の実装を提供します。cipher.NewCTR
関数は、ブロック暗号と初期ベクトル(IV)からCTRモードのストリーム暗号器を作成します。
crypto/rc4
パッケージ (Go言語)
crypto/rc4
: RC4ストリーム暗号の実装を提供します。rc4.NewCipher
関数でRC4暗号器のインスタンスを作成できます。
技術的詳細
このコミットの主要な技術的変更点は、GoのSSHパッケージに複数の新しい暗号アルゴリズムを統合し、それらを柔軟に設定できるようにしたことです。
-
cipher.go
の新規追加:noneCipher
構造体: 暗号化を行わない(コピーのみ)cipher.Stream
の実装。鍵交換前の初期状態や、暗号化が不要な場合に利用されます。newAESCTR
関数: AESブロック暗号とCTRモードを組み合わせてcipher.Stream
を生成します。crypto/aes
とcrypto/cipher
パッケージを利用します。newRC4
関数: RC4ストリーム暗号からcipher.Stream
を生成します。crypto/rc4
パッケージを利用します。cipherMode
構造体: 各暗号アルゴリズムのメタデータ(鍵サイズkeySize
、IVサイズivSize
、スキップバイト数skip
、およびストリーム暗号器を生成する関数createFn
)をカプセル化します。cipherMode.createCipher
メソッド:cipherMode
の情報に基づいて、実際のcipher.Stream
インスタンスを生成します。特に、Arcfourのスキップ処理(RFC 4345で定義される1536バイトの初期キーストリーム破棄)を実装しています。DefaultCipherOrder
変数: OpenSSHのデフォルトクライアントの優先順位に基づいた、サポートされる暗号アルゴリズムのデフォルトリストを定義します。これにはaes128-ctr
,aes192-ctr
,aes256-ctr
,arcfour256
,arcfour128
が含まれます。cipherModes
マップ: 各暗号アルゴリズム名とそれに対応するcipherMode
インスタンスをマッピングします。これにより、アルゴリズム名からその特性と生成関数を動的に取得できるようになります。
-
ClientConfig
およびServerConfig
へのCryptoConfig
の追加:common.go
にCryptoConfig
構造体が新設されました。この構造体は、クライアントとサーバーの両方で共通の暗号関連設定を保持します。CryptoConfig
はCiphers
フィールドを持ち、ユーザーが許可する暗号アルゴリズムのリストを文字列スライスで指定できるようにします。このフィールドが指定されない場合、DefaultCipherOrder
が使用されます。ClientConfig
とServerConfig
にCrypto
フィールド(型はCryptoConfig
)が追加され、SSH接続の暗号設定を外部から制御できるようになりました。
-
鍵交換メッセージ (
kexInitMsg
) の更新:client.go
とserver.go
において、鍵交換初期化メッセージ (kexInitMsg
) のCiphersClientServer
とCiphersServerClient
フィールドが、ハードコードされたsupportedCiphers
ではなく、ClientConfig
またはServerConfig
のCryptoConfig
から取得されるようになりました。これにより、ネゴシエートされる暗号アルゴリズムが設定に基づいて動的に決定されます。
-
transport.go
の変更:packetSizeMultiple
定数が導入され、パディングの計算に使用されます。これは、暗号アルゴリズムによってブロックサイズが異なるため、より柔軟なパディング処理を可能にするための変更です。reader
とwriter
の初期化時に、初期の暗号器としてnoneCipher
が設定されるようになりました。これは、鍵交換が完了するまでは暗号化が行われないためです。setupKeys
関数が大幅に修正されました。以前はAESに特化していましたが、新しいcipherModes
マップとcipherMode.createCipher
メソッドを使用して、ネゴシエートされた暗号アルゴリズムに基づいて適切なストリーム暗号器を動的に生成するようになりました。これにより、複数の暗号アルゴリズムに対応できるようになりました。
-
テストの追加 (
cipher_test.go
):TestCipherReversal
は、各暗号モードが正しく暗号化・復号化できることを検証します。データが暗号化され、その後正しく復号化されて元のデータに戻ることを確認します。TestDefaultCiphersExist
は、DefaultCipherOrder
にリストされているすべての暗号アルゴリズムがcipherModes
マップに存在し、正しく定義されていることを確認します。
これらの変更により、GoのSSHパッケージは、より多くの標準的な暗号アルゴリズムをサポートし、ユーザーが暗号設定を柔軟にカスタマイズできるようになり、セキュリティと相互運用性が向上しました。
コアとなるコードの変更箇所
src/pkg/exp/ssh/cipher.go
(新規追加)
package ssh
import (
"crypto/aes"
"crypto/cipher"
"crypto/rc4"
)
// streamDump is used to dump the initial keystream for stream ciphers. It is a
// a write-only buffer, and not intended for reading so do not require a mutex.
var streamDump [512]byte
// noneCipher implements cipher.Stream and provides no encryption. It is used
// by the transport before the first key-exchange.
type noneCipher struct{}
func (c noneCipher) XORKeyStream(dst, src []byte) {
copy(dst, src)
}
func newAESCTR(key, iv []byte) (cipher.Stream, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cipher.NewCTR(c, iv), nil
}
func newRC4(key, iv []byte) (cipher.Stream, error) {
return rc4.NewCipher(key)
}
type cipherMode struct {
keySize int
ivSize int
skip int
createFn func(key, iv []byte) (cipher.Stream, error)
}
func (c *cipherMode) createCipher(key, iv []byte) (cipher.Stream, error) {
if len(key) < c.keySize {
panic("ssh: key length too small for cipher")
}
if len(iv) < c.ivSize {
panic("ssh: iv too small for cipher")
}
stream, err := c.createFn(key[:c.keySize], iv[:c.ivSize])
if err != nil {
return nil, err
}
for remainingToDump := c.skip; remainingToDump > 0; {
dumpThisTime := remainingToDump
if dumpThisTime > len(streamDump) {
dumpThisTime = len(streamDump)
}
stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
remainingToDump -= dumpThisTime
}
return stream, nil
}
// Specifies a default set of ciphers and a preference order. This is based on
// OpenSSH's default client preference order, minus algorithms that are not
// implemented.
var DefaultCipherOrder = []string{
"aes128-ctr", "aes192-ctr", "aes256-ctr",
"arcfour256", "arcfour128",
}
var cipherModes = map[string]*cipherMode{
// Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms
// are defined in the order specified in the RFC.
"aes128-ctr": &cipherMode{16, aes.BlockSize, 0, newAESCTR},
"aes192-ctr": &cipherMode{24, aes.BlockSize, 0, newAESCTR},
"aes256-ctr": &cipherMode{32, aes.BlockSize, 0, newAESCTR},
// Ciphers from RFC4345, which introduces security-improved arcfour ciphers.
// They are defined in the order specified in the RFC.
"arcfour128": &cipherMode{16, 0, 1536, newRC4},
"arcfour256": &cipherMode{32, 0, 1536, newRC4},
}
src/pkg/exp/ssh/common.go
(変更箇所抜粋)
// Cryptographic configuration common to both ServerConfig and ClientConfig.
type CryptoConfig struct {
// The allowed cipher algorithms. If unspecified then DefaultCipherOrder is
// used.
Ciphers []string
}
func (c *CryptoConfig) ciphers() []string {
if c.Ciphers == nil {
return DefaultCipherOrder
}
return c.Ciphers
}
src/pkg/exp/ssh/transport.go
(変更箇所抜粋)
func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) error {
- h := hashFunc.New()
-
- blockSize := 16
- keySize := 16
macKeySize := 20
- iv := make([]byte, blockSize)
- key := make([]byte, keySize)
+ cipherMode := cipherModes[c.cipherAlgo]
+
+ iv := make([]byte, cipherMode.ivSize)
+ key := make([]byte, cipherMode.keySize)
macKey := make([]byte, macKeySize)
+ h := hashFunc.New()
generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h)
generateKeyMaterial(key, d.keyTag, K, H, sessionId, h)
generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h)
c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)}
- aes, err := aes.NewCipher(key)
+
+ cipher, err := cipherMode.createCipher(key, iv)
if err != nil {
return err
}
- c.cipher = cipher.NewCTR(aes, iv)
+
+ c.cipher = cipher
+
return nil
}
コアとなるコードの解説
src/pkg/exp/ssh/cipher.go
このファイルは、SSHプロトコルで使用される様々な暗号アルゴリズムの実装と管理の中心となります。
noneCipher
: 鍵交換が完了するまでの初期状態や、暗号化が不要な場合のプレースホルダーとして機能します。XORKeyStream
メソッドは単にデータをコピーするだけで、暗号化は行いません。newAESCTR
: AESブロック暗号をCTRモードで動作させるためのストリーム暗号器を生成します。aes.NewCipher
でAES暗号器を作成し、cipher.NewCTR
でそれをCTRモードのストリーム暗号器にラップします。newRC4
: RC4ストリーム暗号器を生成します。cipherMode
構造体: 各暗号アルゴリズムの特性を定義します。keySize
: 暗号鍵のバイト長。ivSize
: 初期ベクトル(IV)のバイト長。CTRモードではIVが重要です。skip
: ストリーム暗号(特にArcfour)で初期キーストリームを破棄するバイト数。RFC 4345で定義されるArcfourのセキュリティ強化のための重要な要素です。createFn
: 実際のcipher.Stream
インスタンスを生成するための関数ポインタ。
cipherMode.createCipher
メソッド:cipherMode
の定義に基づいて、実際の暗号器を生成します。特に注目すべきはfor remainingToDump := c.skip; ...
のループです。これはskip
フィールドが0より大きい場合に、生成されたストリーム暗号器の初期キーストリームを指定されたバイト数だけ「ダンプ」(破棄)する処理です。これにより、Arcfourの既知の脆弱性(初期キーストリームの偏り)を回避します。DefaultCipherOrder
: SSHクライアントがサーバーに提示する暗号アルゴリズムの優先順位リストです。OpenSSHの慣例に従い、よりセキュアでパフォーマンスの良いCTRモードのAESが優先され、次に改良されたArcfourが続きます。cipherModes
マップ: 各暗号アルゴリズム名(例: "aes128-ctr")と、それに対応するcipherMode
インスタンスを関連付けます。これにより、SSHプロトコルネゴシエーションで合意されたアルゴリズム名から、そのアルゴリズムの具体的な実装と特性を動的に取得できるようになります。
src/pkg/exp/ssh/common.go
このファイルでは、クライアントとサーバーで共通の暗号設定をカプセル化する CryptoConfig
構造体が定義されています。
CryptoConfig
構造体:Ciphers []string
: ユーザーが明示的に許可する暗号アルゴリズムのリストを指定できます。このリストがnil
の場合、DefaultCipherOrder
が使用されます。これにより、SSH接続の暗号アルゴリズム選択に柔軟性を持たせることができます。
ciphers()
メソッド:CryptoConfig
のCiphers
フィールドが設定されている場合はその値を返し、設定されていない場合はDefaultCipherOrder
を返します。
src/pkg/exp/ssh/transport.go
このファイルは、SSHトランスポート層の低レベルなデータ送受信と暗号化処理を扱います。
setupKeys
関数: 鍵交換が完了した後、実際にデータを暗号化・復号化するための鍵と暗号器を設定する重要な関数です。- 以前のバージョンではAESに特化していましたが、このコミットにより、
cipherModes[c.cipherAlgo]
を使用して、ネゴシエートされた暗号アルゴリズムに対応するcipherMode
を動的に取得するようになりました。 - 取得した
cipherMode
からivSize
とkeySize
を取得し、それに基づいてIVと鍵を生成します。 - 最も重要な変更は、
cipherMode.createCipher(key, iv)
を呼び出して、ネゴシエートされたアルゴリズムに対応するcipher.Stream
インスタンスを生成する点です。これにより、AES-CTRやArcfourなど、様々な暗号アルゴリズムを透過的に利用できるようになります。 - 生成されたストリーム暗号器は
c.cipher
に設定され、以降のデータ送受信で暗号化・復号化に使用されます。
- 以前のバージョンではAESに特化していましたが、このコミットにより、
これらの変更により、GoのSSHパッケージは、より多くの標準的な暗号アルゴリズムをサポートし、ユーザーが暗号設定を柔軟にカスタマイズできるようになり、セキュリティと相互運用性が向上しました。
関連リンク
- RFC 4253: The Secure Shell (SSH) Transport Layer Protocol
- RFC 4344: The Secure Shell (SSH) Transport Layer Protocol Cryptographic Message Syntax (CMS) for AES-CTR
- RFC 4345: Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol
- GoDoc: crypto/aes
- GoDoc: crypto/cipher
- GoDoc: crypto/rc4
参考にした情報源リンク
- 上記のRFCドキュメント
- Go言語の標準ライブラリのドキュメント (
crypto/aes
,crypto/cipher
,crypto/rc4
) - SSHプロトコルに関する一般的な知識
- OpenSSHの暗号アルゴリズムの選択に関する情報 (OpenSSHのmanページや設定ファイルなど)
- CTRモードとArcfour(RC4)の動作原理に関する暗号学の知識
- Go言語のコードベース (
src/pkg/exp/ssh
ディレクトリ内の他のファイル)