go.crypto/ssh: import gosshnew.

See https://groups.google.com/d/msg/Golang-nuts/AoVxQ4bB5XQ/i8kpMxdbVlEJ

R=hanwen
CC=golang-codereviews
https://golang.org/cl/86190043
This commit is contained in:
Adam Langley
2014-04-09 13:57:52 -07:00
parent 8f45c680ce
commit fa50e7408b
53 changed files with 7391 additions and 3777 deletions
+143 -138
View File
@@ -6,7 +6,9 @@ package ssh
import (
"crypto"
"crypto/rand"
"fmt"
"io"
"sync"
_ "crypto/sha1"
@@ -21,16 +23,39 @@ const (
serviceSSH = "ssh-connection"
)
// supportedCiphers specifies the supported ciphers in preference order.
var supportedCiphers = []string{
"aes128-ctr", "aes192-ctr", "aes256-ctr",
"aes128-gcm@openssh.com",
"arcfour256", "arcfour128",
}
// supportedKexAlgos specifies the supported key-exchange algorithms in
// preference order.
var supportedKexAlgos = []string{
// P384 and P521 are not constant-time yet, but since we don't
// reuse ephemeral keys, using them for ECDH should be OK.
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
kexAlgoDH14SHA1, kexAlgoDH1SHA1,
}
// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods
// of authenticating servers) in preference order.
var supportedHostKeyAlgos = []string{
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
CertAlgoECDSA384v01, CertAlgoECDSA521v01,
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
KeyAlgoRSA, KeyAlgoDSA,
}
// supportedMACs specifies a default set of MAC algorithms in preference order.
// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
// because they have reached the end of their useful life.
var supportedMACs = []string{
"hmac-sha1", "hmac-sha1-96",
}
var supportedCompressions = []string{compressionNone}
// hashFuncs keeps the mapping of supported algorithms to their respective
@@ -48,23 +73,15 @@ var hashFuncs = map[string]crypto.Hash{
CertAlgoECDSA521v01: crypto.SHA512,
}
// UnexpectedMessageError results when the SSH message that we received didn't
// unexpectedMessageError results when the SSH message that we received didn't
// match what we wanted.
type UnexpectedMessageError struct {
expected, got uint8
func unexpectedMessageError(expected, got uint8) error {
return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
}
func (u UnexpectedMessageError) Error() string {
return fmt.Sprintf("ssh: unexpected message type %d (expected %d)", u.got, u.expected)
}
// ParseError results from a malformed SSH message.
type ParseError struct {
msgType uint8
}
func (p ParseError) Error() string {
return fmt.Sprintf("ssh: parse error in message type %d", p.msgType)
// parseError results from a malformed SSH message.
func parseError(tag uint8) error {
return fmt.Errorf("ssh: parse error in message type %d", tag)
}
func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) {
@@ -90,15 +107,17 @@ func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCip
return
}
type directionAlgorithms struct {
Cipher string
MAC string
Compression string
}
type algorithms struct {
kex string
hostKey string
wCipher string
rCipher string
rMAC string
wMAC string
rCompression string
wCompression string
kex string
hostKey string
w directionAlgorithms
r directionAlgorithms
}
func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms) {
@@ -114,32 +133,32 @@ func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algor
return
}
result.wCipher, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
result.w.Cipher, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
if !ok {
return
}
result.rCipher, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
result.r.Cipher, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
if !ok {
return
}
result.wMAC, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
result.w.MAC, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
if !ok {
return
}
result.rMAC, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
result.r.MAC, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
if !ok {
return
}
result.wCompression, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
result.w.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
if !ok {
return
}
result.rCompression, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
result.r.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
if !ok {
return
}
@@ -147,133 +166,87 @@ func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algor
return result
}
// Cryptographic configuration common to both ServerConfig and ClientConfig.
type CryptoConfig struct {
// If rekeythreshold is too small, we can't make any progress sending
// stuff.
const minRekeyThreshold uint64 = 256
// Config contains configuration data common to both ServerConfig and
// ClientConfig.
type Config struct {
// Rand provides the source of entropy for cryptographic
// primitives. If Rand is nil, the cryptographic random reader
// in package crypto/rand will be used.
Rand io.Reader
// The maximum number of bytes sent or received after which a
// new key is negotiated. It must be at least 256. If
// unspecified, 1 gigabyte is used.
RekeyThreshold uint64
// The allowed key exchanges algorithms. If unspecified then a
// default set of algorithms is used.
KeyExchanges []string
// The allowed cipher algorithms. If unspecified then DefaultCipherOrder is
// used.
// The allowed cipher algorithms. If unspecified then a sensible
// default is used.
Ciphers []string
// The allowed MAC algorithms. If unspecified then DefaultMACOrder is used.
// The allowed MAC algorithms. If unspecified then a sensible default
// is used.
MACs []string
}
func (c *CryptoConfig) ciphers() []string {
// SetDefaults sets sensible values for unset fields in config. This is
// exported for testing: Configs passed to SSH functions are copied and have
// default values set automatically.
func (c *Config) SetDefaults() {
if c.Rand == nil {
c.Rand = rand.Reader
}
if c.Ciphers == nil {
return DefaultCipherOrder
c.Ciphers = supportedCiphers
}
return c.Ciphers
}
func (c *CryptoConfig) kexes() []string {
if c.KeyExchanges == nil {
return defaultKeyExchangeOrder
c.KeyExchanges = supportedKexAlgos
}
return c.KeyExchanges
}
func (c *CryptoConfig) macs() []string {
if c.MACs == nil {
return DefaultMACOrder
c.MACs = supportedMACs
}
return c.MACs
}
// serialize a signed slice according to RFC 4254 6.6. The name should
// be a key type name, rather than a cert type name.
func serializeSignature(name string, sig []byte) []byte {
length := stringLength(len(name))
length += stringLength(len(sig))
ret := make([]byte, length)
r := marshalString(ret, []byte(name))
r = marshalString(r, sig)
return ret
}
// MarshalPublicKey serializes a supported key or certificate for use
// by the SSH wire protocol. It can be used for comparison with the
// pubkey argument of ServerConfig's PublicKeyCallback as well as for
// generating an authorized_keys or host_keys file.
func MarshalPublicKey(key PublicKey) []byte {
// See also RFC 4253 6.6.
algoname := key.PublicKeyAlgo()
blob := key.Marshal()
length := stringLength(len(algoname))
length += len(blob)
ret := make([]byte, length)
r := marshalString(ret, []byte(algoname))
copy(r, blob)
return ret
}
// pubAlgoToPrivAlgo returns the private key algorithm format name that
// corresponds to a given public key algorithm format name. For most
// public keys, the private key algorithm name is the same. For some
// situations, such as openssh certificates, the private key algorithm and
// public key algorithm names differ. This accounts for those situations.
func pubAlgoToPrivAlgo(pubAlgo string) string {
switch pubAlgo {
case CertAlgoRSAv01:
return KeyAlgoRSA
case CertAlgoDSAv01:
return KeyAlgoDSA
case CertAlgoECDSA256v01:
return KeyAlgoECDSA256
case CertAlgoECDSA384v01:
return KeyAlgoECDSA384
case CertAlgoECDSA521v01:
return KeyAlgoECDSA521
if c.RekeyThreshold == 0 {
// RFC 4253, section 9 suggests rekeying after 1G.
c.RekeyThreshold = 1 << 30
}
if c.RekeyThreshold < minRekeyThreshold {
c.RekeyThreshold = minRekeyThreshold
}
return pubAlgo
}
// buildDataSignedForAuth returns the data that is signed in order to prove
// possession of a private key. See RFC 4252, section 7.
func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
user := []byte(req.User)
service := []byte(req.Service)
method := []byte(req.Method)
length := stringLength(len(sessionId))
length += 1
length += stringLength(len(user))
length += stringLength(len(service))
length += stringLength(len(method))
length += 1
length += stringLength(len(algo))
length += stringLength(len(pubKey))
ret := make([]byte, length)
r := marshalString(ret, sessionId)
r[0] = msgUserAuthRequest
r = r[1:]
r = marshalString(r, user)
r = marshalString(r, service)
r = marshalString(r, method)
r[0] = 1
r = r[1:]
r = marshalString(r, algo)
r = marshalString(r, pubKey)
return ret
}
// safeString sanitises s according to RFC 4251, section 9.2.
// All control characters except tab, carriage return and newline are
// replaced by 0x20.
func safeString(s string) string {
out := []byte(s)
for i, c := range out {
if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 {
out[i] = 0x20
}
data := struct {
Session []byte
Type byte
User string
Service string
Method string
Sign bool
Algo []byte
PubKey []byte
}{
sessionId,
msgUserAuthRequest,
req.User,
req.Service,
req.Method,
true,
algo,
pubKey,
}
return string(out)
return Marshal(data)
}
func appendU16(buf []byte, n uint16) []byte {
@@ -284,6 +257,12 @@ func appendU32(buf []byte, n uint32) []byte {
return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
}
func appendU64(buf []byte, n uint64) []byte {
return append(buf,
byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
}
func appendInt(buf []byte, n int) []byte {
return appendU32(buf, uint32(n))
}
@@ -296,11 +275,9 @@ func appendString(buf []byte, s string) []byte {
func appendBool(buf []byte, b bool) []byte {
if b {
buf = append(buf, 1)
} else {
buf = append(buf, 0)
return append(buf, 1)
}
return buf
return append(buf, 0)
}
// newCond is a helper to hide the fact that there is no usable zero
@@ -311,7 +288,9 @@ func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
// wishing to write to a channel.
type window struct {
*sync.Cond
win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
writeWaiters int
closed bool
}
// add adds win to the amount of window available
@@ -335,18 +314,44 @@ func (w *window) add(win uint32) bool {
return true
}
// close sets the window to closed, so all reservations fail
// immediately.
func (w *window) close() {
w.L.Lock()
w.closed = true
w.Broadcast()
w.L.Unlock()
}
// reserve reserves win from the available window capacity.
// If no capacity remains, reserve will block. reserve may
// return less than requested.
func (w *window) reserve(win uint32) uint32 {
func (w *window) reserve(win uint32) (uint32, error) {
var err error
w.L.Lock()
for w.win == 0 {
w.writeWaiters++
w.Broadcast()
for w.win == 0 && !w.closed {
w.Wait()
}
w.writeWaiters--
if w.win < win {
win = w.win
}
w.win -= win
if w.closed {
err = io.EOF
}
w.L.Unlock()
return win
return win, err
}
// waitWriterBlocked waits until some goroutine is blocked for further
// writes. It is used in tests only.
func (w *window) waitWriterBlocked() {
w.Cond.L.Lock()
for w.writeWaiters == 0 {
w.Cond.Wait()
}
w.Cond.L.Unlock()
}