github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/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  	"math"
    13  	"sync"
    14  
    15  	_ "crypto/sha1"
    16  	_ "crypto/sha256"
    17  	_ "crypto/sha512"
    18  
    19  	// [Psiphon]
    20  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
    21  )
    22  
    23  // These are string constants in the SSH protocol.
    24  const (
    25  	compressionNone = "none"
    26  	serviceUserAuth = "ssh-userauth"
    27  	serviceSSH      = "ssh-connection"
    28  )
    29  
    30  // supportedCiphers lists ciphers we support but might not recommend.
    31  var supportedCiphers = []string{
    32  	"aes128-ctr", "aes192-ctr", "aes256-ctr",
    33  	"aes128-gcm@openssh.com",
    34  	chacha20Poly1305ID,
    35  	"arcfour256", "arcfour128", "arcfour",
    36  	aes128cbcID,
    37  	tripledescbcID,
    38  }
    39  
    40  // preferredCiphers specifies the default preference for ciphers.
    41  var preferredCiphers = []string{
    42  	"aes128-gcm@openssh.com",
    43  	chacha20Poly1305ID,
    44  	"aes128-ctr", "aes192-ctr", "aes256-ctr",
    45  }
    46  
    47  // supportedKexAlgos specifies the supported key-exchange algorithms in
    48  // preference order.
    49  var supportedKexAlgos = []string{
    50  	kexAlgoCurve25519SHA256,
    51  	// P384 and P521 are not constant-time yet, but since we don't
    52  	// reuse ephemeral keys, using them for ECDH should be OK.
    53  	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
    54  
    55  	// [Psiphon]
    56  	// Remove kexAlgoDH1SHA1 and add kexAlgoDH14SHA256
    57  	kexAlgoDH14SHA256, kexAlgoDH14SHA1,
    58  }
    59  
    60  // serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
    61  // for the server half.
    62  var serverForbiddenKexAlgos = map[string]struct{}{
    63  	kexAlgoDHGEXSHA1:   {}, // server half implementation is only minimal to satisfy the automated tests
    64  	kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
    65  }
    66  
    67  // preferredKexAlgos specifies the default preference for key-exchange algorithms
    68  // in preference order.
    69  var preferredKexAlgos = []string{
    70  	kexAlgoCurve25519SHA256,
    71  	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
    72  
    73  	// [Psiphon]
    74  	// Add kexAlgoDH14SHA256
    75  	kexAlgoDH14SHA256, kexAlgoDH14SHA1,
    76  }
    77  
    78  // supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
    79  // of authenticating servers) in preference order.
    80  var supportedHostKeyAlgos = []string{
    81  	CertSigAlgoRSASHA2512v01, CertSigAlgoRSASHA2256v01,
    82  	CertSigAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
    83  	CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
    84  
    85  	KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
    86  	SigAlgoRSASHA2512, SigAlgoRSASHA2256,
    87  	SigAlgoRSA, KeyAlgoDSA,
    88  
    89  	KeyAlgoED25519,
    90  }
    91  
    92  // supportedMACs specifies a default set of MAC algorithms in preference order.
    93  // This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
    94  // because they have reached the end of their useful life.
    95  var supportedMACs = []string{
    96  	"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96",
    97  }
    98  
    99  var supportedCompressions = []string{compressionNone}
   100  
   101  // hashFuncs keeps the mapping of supported algorithms to their respective
   102  // hashes needed for signature verification.
   103  var hashFuncs = map[string]crypto.Hash{
   104  	SigAlgoRSA:               crypto.SHA1,
   105  	SigAlgoRSASHA2256:        crypto.SHA256,
   106  	SigAlgoRSASHA2512:        crypto.SHA512,
   107  	KeyAlgoDSA:               crypto.SHA1,
   108  	KeyAlgoECDSA256:          crypto.SHA256,
   109  	KeyAlgoECDSA384:          crypto.SHA384,
   110  	KeyAlgoECDSA521:          crypto.SHA512,
   111  	CertSigAlgoRSAv01:        crypto.SHA1,
   112  	CertSigAlgoRSASHA2256v01: crypto.SHA256,
   113  	CertSigAlgoRSASHA2512v01: crypto.SHA512,
   114  	CertAlgoDSAv01:           crypto.SHA1,
   115  	CertAlgoECDSA256v01:      crypto.SHA256,
   116  	CertAlgoECDSA384v01:      crypto.SHA384,
   117  	CertAlgoECDSA521v01:      crypto.SHA512,
   118  }
   119  
   120  // unexpectedMessageError results when the SSH message that we received didn't
   121  // match what we wanted.
   122  func unexpectedMessageError(expected, got uint8) error {
   123  	return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
   124  }
   125  
   126  // parseError results from a malformed SSH message.
   127  func parseError(tag uint8) error {
   128  	return fmt.Errorf("ssh: parse error in message type %d", tag)
   129  }
   130  
   131  func findCommon(what string, client []string, server []string) (common string, err error) {
   132  	for _, c := range client {
   133  		for _, s := range server {
   134  			if c == s {
   135  				return c, nil
   136  			}
   137  		}
   138  	}
   139  	return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
   140  }
   141  
   142  // directionAlgorithms records algorithm choices in one direction (either read or write)
   143  type directionAlgorithms struct {
   144  	Cipher      string
   145  	MAC         string
   146  	Compression string
   147  }
   148  
   149  // rekeyBytes returns a rekeying intervals in bytes.
   150  func (a *directionAlgorithms) rekeyBytes() int64 {
   151  	// According to RFC4344 block ciphers should rekey after
   152  	// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
   153  	// 128.
   154  	switch a.Cipher {
   155  	case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID:
   156  		return 16 * (1 << 32)
   157  
   158  	}
   159  
   160  	// For others, stick with RFC4253 recommendation to rekey after 1 Gb of data.
   161  	return 1 << 30
   162  }
   163  
   164  type algorithms struct {
   165  	kex     string
   166  	hostKey string
   167  	w       directionAlgorithms
   168  	r       directionAlgorithms
   169  }
   170  
   171  func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
   172  	result := &algorithms{}
   173  
   174  	result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
   175  	if err != nil {
   176  		return
   177  	}
   178  
   179  	result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
   180  	if err != nil {
   181  		return
   182  	}
   183  
   184  	stoc, ctos := &result.w, &result.r
   185  	if isClient {
   186  		ctos, stoc = stoc, ctos
   187  	}
   188  
   189  	ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
   190  	if err != nil {
   191  		return
   192  	}
   193  
   194  	stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
   195  	if err != nil {
   196  		return
   197  	}
   198  
   199  	ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
   200  	if err != nil {
   201  		return
   202  	}
   203  
   204  	stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
   205  	if err != nil {
   206  		return
   207  	}
   208  
   209  	ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
   210  	if err != nil {
   211  		return
   212  	}
   213  
   214  	stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
   215  	if err != nil {
   216  		return
   217  	}
   218  
   219  	return result, nil
   220  }
   221  
   222  // If rekeythreshold is too small, we can't make any progress sending
   223  // stuff.
   224  const minRekeyThreshold uint64 = 256
   225  
   226  // Config contains configuration data common to both ServerConfig and
   227  // ClientConfig.
   228  type Config struct {
   229  	// Rand provides the source of entropy for cryptographic
   230  	// primitives. If Rand is nil, the cryptographic random reader
   231  	// in package crypto/rand will be used.
   232  	Rand io.Reader
   233  
   234  	// The maximum number of bytes sent or received after which a
   235  	// new key is negotiated. It must be at least 256. If
   236  	// unspecified, a size suitable for the chosen cipher is used.
   237  	RekeyThreshold uint64
   238  
   239  	// The allowed key exchanges algorithms. If unspecified then a
   240  	// default set of algorithms is used.
   241  	KeyExchanges []string
   242  
   243  	// The allowed cipher algorithms. If unspecified then a sensible
   244  	// default is used.
   245  	Ciphers []string
   246  
   247  	// The allowed MAC algorithms. If unspecified then a sensible default
   248  	// is used.
   249  	MACs []string
   250  
   251  	// [Psiphon]
   252  
   253  	// NoEncryptThenMACHash is used to disable Encrypt-then-MAC hash
   254  	// algorithms.
   255  	NoEncryptThenMACHash bool
   256  
   257  	// KEXPRNGSeed is used for KEX randomization and replay.
   258  	KEXPRNGSeed *prng.Seed
   259  
   260  	// PeerKEXPRNGSeed is used to predict KEX randomization and make
   261  	// adjustments to ensure negotiation succeeds.
   262  	PeerKEXPRNGSeed *prng.Seed
   263  }
   264  
   265  // SetDefaults sets sensible values for unset fields in config. This is
   266  // exported for testing: Configs passed to SSH functions are copied and have
   267  // default values set automatically.
   268  func (c *Config) SetDefaults() {
   269  	if c.Rand == nil {
   270  		c.Rand = rand.Reader
   271  	}
   272  	if c.Ciphers == nil {
   273  		c.Ciphers = preferredCiphers
   274  	}
   275  	var ciphers []string
   276  	for _, c := range c.Ciphers {
   277  		if cipherModes[c] != nil {
   278  			// reject the cipher if we have no cipherModes definition
   279  			ciphers = append(ciphers, c)
   280  		}
   281  	}
   282  	c.Ciphers = ciphers
   283  
   284  	if c.KeyExchanges == nil {
   285  		c.KeyExchanges = preferredKexAlgos
   286  	}
   287  
   288  	if c.MACs == nil {
   289  		c.MACs = supportedMACs
   290  	}
   291  
   292  	if c.RekeyThreshold == 0 {
   293  		// cipher specific default
   294  	} else if c.RekeyThreshold < minRekeyThreshold {
   295  		c.RekeyThreshold = minRekeyThreshold
   296  	} else if c.RekeyThreshold >= math.MaxInt64 {
   297  		// Avoid weirdness if somebody uses -1 as a threshold.
   298  		c.RekeyThreshold = math.MaxInt64
   299  	}
   300  }
   301  
   302  // buildDataSignedForAuth returns the data that is signed in order to prove
   303  // possession of a private key. See RFC 4252, section 7.
   304  func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
   305  	data := struct {
   306  		Session []byte
   307  		Type    byte
   308  		User    string
   309  		Service string
   310  		Method  string
   311  		Sign    bool
   312  		Algo    []byte
   313  		PubKey  []byte
   314  	}{
   315  		sessionID,
   316  		msgUserAuthRequest,
   317  		req.User,
   318  		req.Service,
   319  		req.Method,
   320  		true,
   321  		algo,
   322  		pubKey,
   323  	}
   324  	return Marshal(data)
   325  }
   326  
   327  func appendU16(buf []byte, n uint16) []byte {
   328  	return append(buf, byte(n>>8), byte(n))
   329  }
   330  
   331  func appendU32(buf []byte, n uint32) []byte {
   332  	return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
   333  }
   334  
   335  func appendU64(buf []byte, n uint64) []byte {
   336  	return append(buf,
   337  		byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
   338  		byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
   339  }
   340  
   341  func appendInt(buf []byte, n int) []byte {
   342  	return appendU32(buf, uint32(n))
   343  }
   344  
   345  func appendString(buf []byte, s string) []byte {
   346  	buf = appendU32(buf, uint32(len(s)))
   347  	buf = append(buf, s...)
   348  	return buf
   349  }
   350  
   351  func appendBool(buf []byte, b bool) []byte {
   352  	if b {
   353  		return append(buf, 1)
   354  	}
   355  	return append(buf, 0)
   356  }
   357  
   358  // newCond is a helper to hide the fact that there is no usable zero
   359  // value for sync.Cond.
   360  func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
   361  
   362  // window represents the buffer available to clients
   363  // wishing to write to a channel.
   364  type window struct {
   365  	*sync.Cond
   366  	win          uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
   367  	writeWaiters int
   368  	closed       bool
   369  }
   370  
   371  // add adds win to the amount of window available
   372  // for consumers.
   373  func (w *window) add(win uint32) bool {
   374  	// a zero sized window adjust is a noop.
   375  	if win == 0 {
   376  		return true
   377  	}
   378  	w.L.Lock()
   379  	if w.win+win < win {
   380  		w.L.Unlock()
   381  		return false
   382  	}
   383  	w.win += win
   384  	// It is unusual that multiple goroutines would be attempting to reserve
   385  	// window space, but not guaranteed. Use broadcast to notify all waiters
   386  	// that additional window is available.
   387  	w.Broadcast()
   388  	w.L.Unlock()
   389  	return true
   390  }
   391  
   392  // close sets the window to closed, so all reservations fail
   393  // immediately.
   394  func (w *window) close() {
   395  	w.L.Lock()
   396  	w.closed = true
   397  	w.Broadcast()
   398  	w.L.Unlock()
   399  }
   400  
   401  // reserve reserves win from the available window capacity.
   402  // If no capacity remains, reserve will block. reserve may
   403  // return less than requested.
   404  func (w *window) reserve(win uint32) (uint32, error) {
   405  	var err error
   406  	w.L.Lock()
   407  	w.writeWaiters++
   408  	w.Broadcast()
   409  	for w.win == 0 && !w.closed {
   410  		w.Wait()
   411  	}
   412  	w.writeWaiters--
   413  	if w.win < win {
   414  		win = w.win
   415  	}
   416  	w.win -= win
   417  	if w.closed {
   418  		err = io.EOF
   419  	}
   420  	w.L.Unlock()
   421  	return win, err
   422  }
   423  
   424  // waitWriterBlocked waits until some goroutine is blocked for further
   425  // writes. It is used in tests only.
   426  func (w *window) waitWriterBlocked() {
   427  	w.Cond.L.Lock()
   428  	for w.writeWaiters == 0 {
   429  		w.Cond.Wait()
   430  	}
   431  	w.Cond.L.Unlock()
   432  }