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
This commit is contained in:
Dave Cheney 2012-02-27 19:40:52 -05:00 коммит произвёл Adam Langley
Родитель 9b05c27191
Коммит 6de97b525f
6 изменённых файлов: 99 добавлений и 46 удалений

Просмотреть файл

@ -62,8 +62,8 @@ func (c *ClientConn) handshake() error {
ServerHostKeyAlgos: supportedHostKeyAlgos,
CiphersClientServer: 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,
CompressionServerClient: supportedCompressions,
}

Просмотреть файл

@ -255,3 +255,24 @@ func TestClientAuthRSAandDSA(t *testing.T) {
}
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()
}
}

Просмотреть файл

@ -17,7 +17,6 @@ const (
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
hostAlgoRSA = "ssh-rsa"
hostAlgoDSA = "ssh-dss"
macSHA196 = "hmac-sha1-96"
compressionNone = "none"
serviceUserAuth = "ssh-userauth"
serviceSSH = "ssh-connection"
@ -25,7 +24,6 @@ const (
var supportedKexAlgos = []string{kexAlgoDH14SHA1}
var supportedHostKeyAlgos = []string{hostAlgoRSA}
var supportedMACs = []string{macSHA196}
var supportedCompressions = []string{compressionNone}
// 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
// used.
Ciphers []string
// The allowed MAC algorithms. If unspecified then DefaultMACOrder is used.
MACs []string
}
func (c *CryptoConfig) ciphers() []string {
@ -143,6 +144,13 @@ func (c *CryptoConfig) ciphers() []string {
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.
func serializeSignature(algoname string, sig []byte) []byte {
switch algoname {

58
ssh/mac.go Normal file
Просмотреть файл

@ -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)}
}},
}

Просмотреть файл

@ -228,8 +228,8 @@ func (s *ServerConn) Handshake() error {
ServerHostKeyAlgos: supportedHostKeyAlgos,
CiphersClientServer: 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,
CompressionServerClient: supportedCompressions,
}

Просмотреть файл

@ -8,8 +8,6 @@ import (
"bufio"
"crypto"
"crypto/cipher"
"crypto/hmac"
"crypto/sha1"
"crypto/subtle"
"errors"
"hash"
@ -255,28 +253,22 @@ var (
// (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 {
cipherMode := cipherModes[c.cipherAlgo]
macKeySize := 20
macMode := macModes[c.macAlgo]
iv := make([]byte, cipherMode.ivSize)
key := make([]byte, cipherMode.keySize)
macKey := make([]byte, macKeySize)
macKey := make([]byte, macMode.keySize)
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.New(sha1.New, macKey)}
c.mac = macMode.new(macKey)
cipher, err := cipherMode.createCipher(key, iv)
if err != nil {
return err
}
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
@ -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
// 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