瀏覽代碼

go.crypto/ssh: improve support for MAC algorithms

Also, add support for hmac-sha1.

At the suggestion of AGL hmac-md5, and hmac-md5-96
support was not included.

Fixes golang/go#3095.

R=golang-dev, agl, huin
CC=golang-dev
https://golang.org/cl/5696065
Dave Cheney 13 年之前
父節點
當前提交
6de97b525f
共有 6 個文件被更改,包括 99 次插入46 次删除
  1. 2 2
      ssh/client.go
  2. 21 0
      ssh/client_auth_test.go
  3. 10 2
      ssh/common.go
  4. 58 0
      ssh/mac.go
  5. 2 2
      ssh/server.go
  6. 6 40
      ssh/transport.go

+ 2 - 2
ssh/client.go

@@ -62,8 +62,8 @@ func (c *ClientConn) handshake() error {
 		ServerHostKeyAlgos:      supportedHostKeyAlgos,
 		ServerHostKeyAlgos:      supportedHostKeyAlgos,
 		CiphersClientServer:     c.config.Crypto.ciphers(),
 		CiphersClientServer:     c.config.Crypto.ciphers(),
 		CiphersServerClient:     c.config.Crypto.ciphers(),
 		CiphersServerClient:     c.config.Crypto.ciphers(),
-		MACsClientServer:        supportedMACs,
-		MACsServerClient:        supportedMACs,
+		MACsClientServer:        c.config.Crypto.macs(),
+		MACsServerClient:        c.config.Crypto.macs(),
 		CompressionClientServer: supportedCompressions,
 		CompressionClientServer: supportedCompressions,
 		CompressionServerClient: supportedCompressions,
 		CompressionServerClient: supportedCompressions,
 	}
 	}

+ 21 - 0
ssh/client_auth_test.go

@@ -255,3 +255,24 @@ func TestClientAuthRSAandDSA(t *testing.T) {
 	}
 	}
 	c.Close()
 	c.Close()
 }
 }
+
+func TestClientHMAC(t *testing.T) {
+	kc := new(keychain)
+	kc.keys = append(kc.keys, rsakey)
+	for _, mac := range DefaultMACOrder {
+		config := &ClientConfig{
+			User: "testuser",
+			Auth: []ClientAuth{
+				ClientAuthKeyring(kc),
+			},
+			Crypto: CryptoConfig{
+				MACs: []string{mac},
+			},
+		}
+		c, err := Dial("tcp", newMockAuthServer(t), config)
+		if err != nil {
+			t.Fatalf("client could not authenticate with mac algo %s: %v", mac, err)
+		}
+		c.Close()
+	}
+}

+ 10 - 2
ssh/common.go

@@ -17,7 +17,6 @@ const (
 	kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
 	kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
 	hostAlgoRSA     = "ssh-rsa"
 	hostAlgoRSA     = "ssh-rsa"
 	hostAlgoDSA     = "ssh-dss"
 	hostAlgoDSA     = "ssh-dss"
-	macSHA196       = "hmac-sha1-96"
 	compressionNone = "none"
 	compressionNone = "none"
 	serviceUserAuth = "ssh-userauth"
 	serviceUserAuth = "ssh-userauth"
 	serviceSSH      = "ssh-connection"
 	serviceSSH      = "ssh-connection"
@@ -25,7 +24,6 @@ const (
 
 
 var supportedKexAlgos = []string{kexAlgoDH14SHA1}
 var supportedKexAlgos = []string{kexAlgoDH14SHA1}
 var supportedHostKeyAlgos = []string{hostAlgoRSA}
 var supportedHostKeyAlgos = []string{hostAlgoRSA}
-var supportedMACs = []string{macSHA196}
 var supportedCompressions = []string{compressionNone}
 var supportedCompressions = []string{compressionNone}
 
 
 // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
 // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
@@ -134,6 +132,9 @@ type CryptoConfig struct {
 	// The allowed cipher algorithms. If unspecified then DefaultCipherOrder is
 	// The allowed cipher algorithms. If unspecified then DefaultCipherOrder is
 	// used.
 	// used.
 	Ciphers []string
 	Ciphers []string
+
+	// The allowed MAC algorithms. If unspecified then DefaultMACOrder is used.
+	MACs []string
 }
 }
 
 
 func (c *CryptoConfig) ciphers() []string {
 func (c *CryptoConfig) ciphers() []string {
@@ -143,6 +144,13 @@ func (c *CryptoConfig) ciphers() []string {
 	return c.Ciphers
 	return c.Ciphers
 }
 }
 
 
+func (c *CryptoConfig) macs() []string {
+	if c.MACs == nil {
+		return DefaultMACOrder
+	}
+	return c.MACs
+}
+
 // serialize a signed slice according to RFC 4254 6.6.
 // serialize a signed slice according to RFC 4254 6.6.
 func serializeSignature(algoname string, sig []byte) []byte {
 func serializeSignature(algoname string, sig []byte) []byte {
 	switch algoname {
 	switch algoname {

+ 58 - 0
ssh/mac.go

@@ -0,0 +1,58 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssh
+
+// Message authentication support
+
+import (
+	"crypto/hmac"
+	"crypto/sha1"
+	"hash"
+)
+
+type macMode struct {
+	keySize int
+	new     func(key []byte) hash.Hash
+}
+
+// truncatingMAC wraps around a hash.Hash and truncates the output digest to
+// a given size.
+type truncatingMAC struct {
+	length int
+	hmac   hash.Hash
+}
+
+func (t truncatingMAC) Write(data []byte) (int, error) {
+	return t.hmac.Write(data)
+}
+
+func (t truncatingMAC) Sum(in []byte) []byte {
+	out := t.hmac.Sum(in)
+	return out[:len(in)+t.length]
+}
+
+func (t truncatingMAC) Reset() {
+	t.hmac.Reset()
+}
+
+func (t truncatingMAC) Size() int {
+	return t.length
+}
+
+func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
+
+// Specifies a default set of MAC algorithms and a preference order. 
+// This is based on RFC 4253, section 6.4, with the removal of the 
+// hmac-md5 variants as they have reached the end of their useful life.
+var DefaultMACOrder = []string{"hmac-sha1", "hmac-sha1-96"}
+
+var macModes = map[string]*macMode{
+	"hmac-sha1": {20, func(key []byte) hash.Hash {
+		return hmac.New(sha1.New, key)
+	}},
+	"hmac-sha1-96": {20, func(key []byte) hash.Hash {
+		return truncatingMAC{12, hmac.New(sha1.New, key)}
+	}},
+}

+ 2 - 2
ssh/server.go

@@ -228,8 +228,8 @@ func (s *ServerConn) Handshake() error {
 		ServerHostKeyAlgos:      supportedHostKeyAlgos,
 		ServerHostKeyAlgos:      supportedHostKeyAlgos,
 		CiphersClientServer:     s.config.Crypto.ciphers(),
 		CiphersClientServer:     s.config.Crypto.ciphers(),
 		CiphersServerClient:     s.config.Crypto.ciphers(),
 		CiphersServerClient:     s.config.Crypto.ciphers(),
-		MACsClientServer:        supportedMACs,
-		MACsServerClient:        supportedMACs,
+		MACsClientServer:        s.config.Crypto.macs(),
+		MACsServerClient:        s.config.Crypto.macs(),
 		CompressionClientServer: supportedCompressions,
 		CompressionClientServer: supportedCompressions,
 		CompressionServerClient: supportedCompressions,
 		CompressionServerClient: supportedCompressions,
 	}
 	}

+ 6 - 40
ssh/transport.go

@@ -8,8 +8,6 @@ import (
 	"bufio"
 	"bufio"
 	"crypto"
 	"crypto"
 	"crypto/cipher"
 	"crypto/cipher"
-	"crypto/hmac"
-	"crypto/sha1"
 	"crypto/subtle"
 	"crypto/subtle"
 	"errors"
 	"errors"
 	"hash"
 	"hash"
@@ -255,28 +253,22 @@ var (
 // (to setup server->client keys) or clientKeys (for client->server keys).
 // (to setup server->client keys) or clientKeys (for client->server keys).
 func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) error {
 func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) error {
 	cipherMode := cipherModes[c.cipherAlgo]
 	cipherMode := cipherModes[c.cipherAlgo]
-
-	macKeySize := 20
+	macMode := macModes[c.macAlgo]
 
 
 	iv := make([]byte, cipherMode.ivSize)
 	iv := make([]byte, cipherMode.ivSize)
 	key := make([]byte, cipherMode.keySize)
 	key := make([]byte, cipherMode.keySize)
-	macKey := make([]byte, macKeySize)
+	macKey := make([]byte, macMode.keySize)
 
 
 	h := hashFunc.New()
 	h := hashFunc.New()
 	generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h)
 	generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h)
 	generateKeyMaterial(key, d.keyTag, K, H, sessionId, h)
 	generateKeyMaterial(key, d.keyTag, K, H, sessionId, h)
 	generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h)
 	generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h)
 
 
-	c.mac = truncatingMAC{12, hmac.New(sha1.New, macKey)}
-
-	cipher, err := cipherMode.createCipher(key, iv)
-	if err != nil {
-		return err
-	}
+	c.mac = macMode.new(macKey)
 
 
-	c.cipher = cipher
-
-	return nil
+	var err error
+	c.cipher, err = cipherMode.createCipher(key, iv)
+	return err
 }
 }
 
 
 // generateKeyMaterial fills out with key material generated from tag, K, H
 // generateKeyMaterial fills out with key material generated from tag, K, H
@@ -305,32 +297,6 @@ func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) {
 	}
 	}
 }
 }
 
 
-// truncatingMAC wraps around a hash.Hash and truncates the output digest to
-// a given size.
-type truncatingMAC struct {
-	length int
-	hmac   hash.Hash
-}
-
-func (t truncatingMAC) Write(data []byte) (int, error) {
-	return t.hmac.Write(data)
-}
-
-func (t truncatingMAC) Sum(in []byte) []byte {
-	out := t.hmac.Sum(in)
-	return out[:len(in)+t.length]
-}
-
-func (t truncatingMAC) Reset() {
-	t.hmac.Reset()
-}
-
-func (t truncatingMAC) Size() int {
-	return t.length
-}
-
-func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
-
 // maxVersionStringBytes is the maximum number of bytes that we'll accept as a
 // maxVersionStringBytes is the maximum number of bytes that we'll accept as a
 // version string. In the event that the client is talking a different protocol
 // version string. In the event that the client is talking a different protocol
 // we need to set a limit otherwise we will keep using more and more memory
 // we need to set a limit otherwise we will keep using more and more memory