github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/crypto/ssh/common.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssh
     6  
     7  import (
     8  	"crypto"
     9  	"crypto/rand"
    10  	"fmt"
    11  	"io"
    12  	"sync"
    13  
    14  	_ "crypto/sha1"
    15  	_ "crypto/sha256"
    16  	_ "crypto/sha512"
    17  )
    18  
    19  // These are string constants in the SSH protocol.
    20  const (
    21  	compressionNone = "none"
    22  	serviceUserAuth = "ssh-userauth"
    23  	serviceSSH      = "ssh-connection"
    24  )
    25  
    26  // supportedCiphers specifies the supported ciphers in preference order.
    27  var supportedCiphers = []string{
    28  	"aes128-ctr", "aes192-ctr", "aes256-ctr",
    29  	"aes128-gcm@openssh.com",
    30  	"arcfour256", "arcfour128",
    31  }
    32  
    33  // supportedKexAlgos specifies the supported key-exchange algorithms in
    34  // preference order.
    35  var supportedKexAlgos = []string{
    36  	kexAlgoCurve25519SHA256,
    37  	// P384 and P521 are not constant-time yet, but since we don't
    38  	// reuse ephemeral keys, using them for ECDH should be OK.
    39  	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
    40  	kexAlgoDH14SHA1, kexAlgoDH1SHA1,
    41  }
    42  
    43  // supportedKexAlgos specifies the supported host-key algorithms (i.e. methods
    44  // of authenticating servers) in preference order.
    45  var supportedHostKeyAlgos = []string{
    46  	CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
    47  	CertAlgoECDSA384v01, CertAlgoECDSA521v01,
    48  
    49  	KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
    50  	KeyAlgoRSA, KeyAlgoDSA,
    51  }
    52  
    53  // supportedMACs specifies a default set of MAC algorithms in preference order.
    54  // This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
    55  // because they have reached the end of their useful life.
    56  var supportedMACs = []string{
    57  	"hmac-sha2-256", "hmac-sha1", "hmac-sha1-96",
    58  }
    59  
    60  var supportedCompressions = []string{compressionNone}
    61  
    62  // hashFuncs keeps the mapping of supported algorithms to their respective
    63  // hashes needed for signature verification.
    64  var hashFuncs = map[string]crypto.Hash{
    65  	KeyAlgoRSA:          crypto.SHA1,
    66  	KeyAlgoDSA:          crypto.SHA1,
    67  	KeyAlgoECDSA256:     crypto.SHA256,
    68  	KeyAlgoECDSA384:     crypto.SHA384,
    69  	KeyAlgoECDSA521:     crypto.SHA512,
    70  	CertAlgoRSAv01:      crypto.SHA1,
    71  	CertAlgoDSAv01:      crypto.SHA1,
    72  	CertAlgoECDSA256v01: crypto.SHA256,
    73  	CertAlgoECDSA384v01: crypto.SHA384,
    74  	CertAlgoECDSA521v01: crypto.SHA512,
    75  }
    76  
    77  // unexpectedMessageError results when the SSH message that we received didn't
    78  // match what we wanted.
    79  func unexpectedMessageError(expected, got uint8) error {
    80  	return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
    81  }
    82  
    83  // parseError results from a malformed SSH message.
    84  func parseError(tag uint8) error {
    85  	return fmt.Errorf("ssh: parse error in message type %d", tag)
    86  }
    87  
    88  func findCommon(what string, client []string, server []string) (common string, err error) {
    89  	for _, c := range client {
    90  		for _, s := range server {
    91  			if c == s {
    92  				return c, nil
    93  			}
    94  		}
    95  	}
    96  	return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
    97  }
    98  
    99  type directionAlgorithms struct {
   100  	Cipher      string
   101  	MAC         string
   102  	Compression string
   103  }
   104  
   105  type algorithms struct {
   106  	kex     string
   107  	hostKey string
   108  	w       directionAlgorithms
   109  	r       directionAlgorithms
   110  }
   111  
   112  func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
   113  	result := &algorithms{}
   114  
   115  	result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
   116  	if err != nil {
   117  		return
   118  	}
   119  
   120  	result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
   121  	if err != nil {
   122  		return
   123  	}
   124  
   125  	result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
   126  	if err != nil {
   127  		return
   128  	}
   129  
   130  	result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
   131  	if err != nil {
   132  		return
   133  	}
   134  
   135  	result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
   136  	if err != nil {
   137  		return
   138  	}
   139  
   140  	result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
   141  	if err != nil {
   142  		return
   143  	}
   144  
   145  	result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
   146  	if err != nil {
   147  		return
   148  	}
   149  
   150  	result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
   151  	if err != nil {
   152  		return
   153  	}
   154  
   155  	return result, nil
   156  }
   157  
   158  // If rekeythreshold is too small, we can't make any progress sending
   159  // stuff.
   160  const minRekeyThreshold uint64 = 256
   161  
   162  // Config contains configuration data common to both ServerConfig and
   163  // ClientConfig.
   164  type Config struct {
   165  	// Rand provides the source of entropy for cryptographic
   166  	// primitives. If Rand is nil, the cryptographic random reader
   167  	// in package crypto/rand will be used.
   168  	Rand io.Reader
   169  
   170  	// The maximum number of bytes sent or received after which a
   171  	// new key is negotiated. It must be at least 256. If
   172  	// unspecified, 1 gigabyte is used.
   173  	RekeyThreshold uint64
   174  
   175  	// The allowed key exchanges algorithms. If unspecified then a
   176  	// default set of algorithms is used.
   177  	KeyExchanges []string
   178  
   179  	// The allowed cipher algorithms. If unspecified then a sensible
   180  	// default is used.
   181  	Ciphers []string
   182  
   183  	// The allowed MAC algorithms. If unspecified then a sensible default
   184  	// is used.
   185  	MACs []string
   186  }
   187  
   188  // SetDefaults sets sensible values for unset fields in config. This is
   189  // exported for testing: Configs passed to SSH functions are copied and have
   190  // default values set automatically.
   191  func (c *Config) SetDefaults() {
   192  	if c.Rand == nil {
   193  		c.Rand = rand.Reader
   194  	}
   195  	if c.Ciphers == nil {
   196  		c.Ciphers = supportedCiphers
   197  	}
   198  	var ciphers []string
   199  	for _, c := range c.Ciphers {
   200  		if cipherModes[c] != nil {
   201  			// reject the cipher if we have no cipherModes definition
   202  			ciphers = append(ciphers, c)
   203  		}
   204  	}
   205  	c.Ciphers = ciphers
   206  
   207  	if c.KeyExchanges == nil {
   208  		c.KeyExchanges = supportedKexAlgos
   209  	}
   210  
   211  	if c.MACs == nil {
   212  		c.MACs = supportedMACs
   213  	}
   214  
   215  	if c.RekeyThreshold == 0 {
   216  		// RFC 4253, section 9 suggests rekeying after 1G.
   217  		c.RekeyThreshold = 1 << 30
   218  	}
   219  	if c.RekeyThreshold < minRekeyThreshold {
   220  		c.RekeyThreshold = minRekeyThreshold
   221  	}
   222  }
   223  
   224  // buildDataSignedForAuth returns the data that is signed in order to prove
   225  // possession of a private key. See RFC 4252, section 7.
   226  func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
   227  	data := struct {
   228  		Session []byte
   229  		Type    byte
   230  		User    string
   231  		Service string
   232  		Method  string
   233  		Sign    bool
   234  		Algo    []byte
   235  		PubKey  []byte
   236  	}{
   237  		sessionId,
   238  		msgUserAuthRequest,
   239  		req.User,
   240  		req.Service,
   241  		req.Method,
   242  		true,
   243  		algo,
   244  		pubKey,
   245  	}
   246  	return Marshal(data)
   247  }
   248  
   249  func appendU16(buf []byte, n uint16) []byte {
   250  	return append(buf, byte(n>>8), byte(n))
   251  }
   252  
   253  func appendU32(buf []byte, n uint32) []byte {
   254  	return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
   255  }
   256  
   257  func appendU64(buf []byte, n uint64) []byte {
   258  	return append(buf,
   259  		byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
   260  		byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
   261  }
   262  
   263  func appendInt(buf []byte, n int) []byte {
   264  	return appendU32(buf, uint32(n))
   265  }
   266  
   267  func appendString(buf []byte, s string) []byte {
   268  	buf = appendU32(buf, uint32(len(s)))
   269  	buf = append(buf, s...)
   270  	return buf
   271  }
   272  
   273  func appendBool(buf []byte, b bool) []byte {
   274  	if b {
   275  		return append(buf, 1)
   276  	}
   277  	return append(buf, 0)
   278  }
   279  
   280  // newCond is a helper to hide the fact that there is no usable zero
   281  // value for sync.Cond.
   282  func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
   283  
   284  // window represents the buffer available to clients
   285  // wishing to write to a channel.
   286  type window struct {
   287  	*sync.Cond
   288  	win          uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
   289  	writeWaiters int
   290  	closed       bool
   291  }
   292  
   293  // add adds win to the amount of window available
   294  // for consumers.
   295  func (w *window) add(win uint32) bool {
   296  	// a zero sized window adjust is a noop.
   297  	if win == 0 {
   298  		return true
   299  	}
   300  	w.L.Lock()
   301  	if w.win+win < win {
   302  		w.L.Unlock()
   303  		return false
   304  	}
   305  	w.win += win
   306  	// It is unusual that multiple goroutines would be attempting to reserve
   307  	// window space, but not guaranteed. Use broadcast to notify all waiters
   308  	// that additional window is available.
   309  	w.Broadcast()
   310  	w.L.Unlock()
   311  	return true
   312  }
   313  
   314  // close sets the window to closed, so all reservations fail
   315  // immediately.
   316  func (w *window) close() {
   317  	w.L.Lock()
   318  	w.closed = true
   319  	w.Broadcast()
   320  	w.L.Unlock()
   321  }
   322  
   323  // reserve reserves win from the available window capacity.
   324  // If no capacity remains, reserve will block. reserve may
   325  // return less than requested.
   326  func (w *window) reserve(win uint32) (uint32, error) {
   327  	var err error
   328  	w.L.Lock()
   329  	w.writeWaiters++
   330  	w.Broadcast()
   331  	for w.win == 0 && !w.closed {
   332  		w.Wait()
   333  	}
   334  	w.writeWaiters--
   335  	if w.win < win {
   336  		win = w.win
   337  	}
   338  	w.win -= win
   339  	if w.closed {
   340  		err = io.EOF
   341  	}
   342  	w.L.Unlock()
   343  	return win, err
   344  }
   345  
   346  // waitWriterBlocked waits until some goroutine is blocked for further
   347  // writes. It is used in tests only.
   348  func (w *window) waitWriterBlocked() {
   349  	w.Cond.L.Lock()
   350  	for w.writeWaiters == 0 {
   351  		w.Cond.Wait()
   352  	}
   353  	w.Cond.L.Unlock()
   354  }