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
  5package ssh
  6
  7import (
  8	"crypto"
  9	"crypto/fips140"
 10	"crypto/rand"
 11	"fmt"
 12	"io"
 13	"math"
 14	"slices"
 15	"sync"
 16
 17	_ "crypto/sha1"
 18	_ "crypto/sha256"
 19	_ "crypto/sha512"
 20)
 21
 22// These are string constants in the SSH protocol.
 23const (
 24	compressionNone = "none"
 25	serviceUserAuth = "ssh-userauth"
 26	serviceSSH      = "ssh-connection"
 27)
 28
 29// The ciphers currently or previously implemented by this library, to use in
 30// [Config.Ciphers]. For a list, see the [Algorithms.Ciphers] returned by
 31// [SupportedAlgorithms] or [InsecureAlgorithms].
 32const (
 33	CipherAES128GCM            = "aes128-gcm@openssh.com"
 34	CipherAES256GCM            = "aes256-gcm@openssh.com"
 35	CipherChaCha20Poly1305     = "chacha20-poly1305@openssh.com"
 36	CipherAES128CTR            = "aes128-ctr"
 37	CipherAES192CTR            = "aes192-ctr"
 38	CipherAES256CTR            = "aes256-ctr"
 39	InsecureCipherAES128CBC    = "aes128-cbc"
 40	InsecureCipherTripleDESCBC = "3des-cbc"
 41	InsecureCipherRC4          = "arcfour"
 42	InsecureCipherRC4128       = "arcfour128"
 43	InsecureCipherRC4256       = "arcfour256"
 44)
 45
 46// The key exchanges currently or previously implemented by this library, to use
 47// in [Config.KeyExchanges]. For a list, see the
 48// [Algorithms.KeyExchanges] returned by [SupportedAlgorithms] or
 49// [InsecureAlgorithms].
 50const (
 51	InsecureKeyExchangeDH1SHA1   = "diffie-hellman-group1-sha1"
 52	InsecureKeyExchangeDH14SHA1  = "diffie-hellman-group14-sha1"
 53	KeyExchangeDH14SHA256        = "diffie-hellman-group14-sha256"
 54	KeyExchangeDH16SHA512        = "diffie-hellman-group16-sha512"
 55	KeyExchangeECDHP256          = "ecdh-sha2-nistp256"
 56	KeyExchangeECDHP384          = "ecdh-sha2-nistp384"
 57	KeyExchangeECDHP521          = "ecdh-sha2-nistp521"
 58	KeyExchangeCurve25519        = "curve25519-sha256"
 59	InsecureKeyExchangeDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
 60	KeyExchangeDHGEXSHA256       = "diffie-hellman-group-exchange-sha256"
 61	// KeyExchangeMLKEM768X25519 is supported from Go 1.24.
 62	KeyExchangeMLKEM768X25519 = "mlkem768x25519-sha256"
 63
 64	// An alias for KeyExchangeCurve25519SHA256. This kex ID will be added if
 65	// KeyExchangeCurve25519SHA256 is requested for backward compatibility with
 66	// OpenSSH versions up to 7.2.
 67	keyExchangeCurve25519LibSSH = "curve25519-sha256@libssh.org"
 68)
 69
 70// The message authentication code (MAC) currently or previously implemented by
 71// this library, to use in [Config.MACs]. For a list, see the
 72// [Algorithms.MACs] returned by [SupportedAlgorithms] or
 73// [InsecureAlgorithms].
 74const (
 75	HMACSHA256ETM      = "hmac-sha2-256-etm@openssh.com"
 76	HMACSHA512ETM      = "hmac-sha2-512-etm@openssh.com"
 77	HMACSHA256         = "hmac-sha2-256"
 78	HMACSHA512         = "hmac-sha2-512"
 79	HMACSHA1           = "hmac-sha1"
 80	InsecureHMACSHA196 = "hmac-sha1-96"
 81)
 82
 83var (
 84	// supportedKexAlgos specifies key-exchange algorithms implemented by this
 85	// package in preference order, excluding those with security issues.
 86	supportedKexAlgos = []string{
 87		KeyExchangeMLKEM768X25519,
 88		KeyExchangeCurve25519,
 89		KeyExchangeECDHP256,
 90		KeyExchangeECDHP384,
 91		KeyExchangeECDHP521,
 92		KeyExchangeDH14SHA256,
 93		KeyExchangeDH16SHA512,
 94		KeyExchangeDHGEXSHA256,
 95	}
 96	// defaultKexAlgos specifies the default preference for key-exchange
 97	// algorithms in preference order.
 98	defaultKexAlgos = []string{
 99		KeyExchangeMLKEM768X25519,
100		KeyExchangeCurve25519,
101		KeyExchangeECDHP256,
102		KeyExchangeECDHP384,
103		KeyExchangeECDHP521,
104		KeyExchangeDH14SHA256,
105		InsecureKeyExchangeDH14SHA1,
106	}
107	// insecureKexAlgos specifies key-exchange algorithms implemented by this
108	// package and which have security issues.
109	insecureKexAlgos = []string{
110		InsecureKeyExchangeDH14SHA1,
111		InsecureKeyExchangeDH1SHA1,
112		InsecureKeyExchangeDHGEXSHA1,
113	}
114	// supportedCiphers specifies cipher algorithms implemented by this package
115	// in preference order, excluding those with security issues.
116	supportedCiphers = []string{
117		CipherAES128GCM,
118		CipherAES256GCM,
119		CipherChaCha20Poly1305,
120		CipherAES128CTR,
121		CipherAES192CTR,
122		CipherAES256CTR,
123	}
124	// defaultCiphers specifies the default preference for ciphers algorithms
125	// in preference order.
126	defaultCiphers = supportedCiphers
127	// insecureCiphers specifies cipher algorithms implemented by this
128	// package and which have security issues.
129	insecureCiphers = []string{
130		InsecureCipherAES128CBC,
131		InsecureCipherTripleDESCBC,
132		InsecureCipherRC4256,
133		InsecureCipherRC4128,
134		InsecureCipherRC4,
135	}
136	// supportedMACs specifies MAC algorithms implemented by this package in
137	// preference order, excluding those with security issues.
138	supportedMACs = []string{
139		HMACSHA256ETM,
140		HMACSHA512ETM,
141		HMACSHA256,
142		HMACSHA512,
143		HMACSHA1,
144	}
145	// defaultMACs specifies the default preference for MAC algorithms in
146	// preference order.
147	defaultMACs = []string{
148		HMACSHA256ETM,
149		HMACSHA512ETM,
150		HMACSHA256,
151		HMACSHA512,
152		HMACSHA1,
153		InsecureHMACSHA196,
154	}
155	// insecureMACs specifies MAC algorithms implemented by this
156	// package and which have security issues.
157	insecureMACs = []string{
158		InsecureHMACSHA196,
159	}
160	// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e.
161	// methods of authenticating servers) implemented by this package in
162	// preference order, excluding those with security issues.
163	supportedHostKeyAlgos = []string{
164		CertAlgoRSASHA256v01,
165		CertAlgoRSASHA512v01,
166		CertAlgoECDSA256v01,
167		CertAlgoECDSA384v01,
168		CertAlgoECDSA521v01,
169		CertAlgoED25519v01,
170		KeyAlgoRSASHA256,
171		KeyAlgoRSASHA512,
172		KeyAlgoECDSA256,
173		KeyAlgoECDSA384,
174		KeyAlgoECDSA521,
175		KeyAlgoED25519,
176	}
177	// defaultHostKeyAlgos specifies the default preference for host-key
178	// algorithms in preference order.
179	defaultHostKeyAlgos = []string{
180		CertAlgoRSASHA256v01,
181		CertAlgoRSASHA512v01,
182		CertAlgoRSAv01,
183		InsecureCertAlgoDSAv01,
184		CertAlgoECDSA256v01,
185		CertAlgoECDSA384v01,
186		CertAlgoECDSA521v01,
187		CertAlgoED25519v01,
188		KeyAlgoECDSA256,
189		KeyAlgoECDSA384,
190		KeyAlgoECDSA521,
191		KeyAlgoRSASHA256,
192		KeyAlgoRSASHA512,
193		KeyAlgoRSA,
194		InsecureKeyAlgoDSA,
195		KeyAlgoED25519,
196	}
197	// insecureHostKeyAlgos specifies host-key algorithms implemented by this
198	// package and which have security issues.
199	insecureHostKeyAlgos = []string{
200		KeyAlgoRSA,
201		InsecureKeyAlgoDSA,
202		CertAlgoRSAv01,
203		InsecureCertAlgoDSAv01,
204	}
205	// supportedPubKeyAuthAlgos specifies the supported client public key
206	// authentication algorithms. Note that this doesn't include certificate
207	// types since those use the underlying algorithm. Order is irrelevant.
208	supportedPubKeyAuthAlgos = []string{
209		KeyAlgoED25519,
210		KeyAlgoSKED25519,
211		KeyAlgoSKECDSA256,
212		KeyAlgoECDSA256,
213		KeyAlgoECDSA384,
214		KeyAlgoECDSA521,
215		KeyAlgoRSASHA256,
216		KeyAlgoRSASHA512,
217	}
218
219	// defaultPubKeyAuthAlgos specifies the preferred client public key
220	// authentication algorithms. This list is sent to the client if it supports
221	// the server-sig-algs extension. Order is irrelevant.
222	defaultPubKeyAuthAlgos = []string{
223		KeyAlgoED25519,
224		KeyAlgoSKED25519,
225		KeyAlgoSKECDSA256,
226		KeyAlgoECDSA256,
227		KeyAlgoECDSA384,
228		KeyAlgoECDSA521,
229		KeyAlgoRSASHA256,
230		KeyAlgoRSASHA512,
231		KeyAlgoRSA,
232		InsecureKeyAlgoDSA,
233	}
234	// insecurePubKeyAuthAlgos specifies client public key authentication
235	// algorithms implemented by this package and which have security issues.
236	insecurePubKeyAuthAlgos = []string{
237		KeyAlgoRSA,
238		InsecureKeyAlgoDSA,
239	}
240)
241
242// NegotiatedAlgorithms defines algorithms negotiated between client and server.
243type NegotiatedAlgorithms struct {
244	KeyExchange string
245	HostKey     string
246	Read        DirectionAlgorithms
247	Write       DirectionAlgorithms
248}
249
250// Algorithms defines a set of algorithms that can be configured in the client
251// or server config for negotiation during a handshake.
252type Algorithms struct {
253	KeyExchanges   []string
254	Ciphers        []string
255	MACs           []string
256	HostKeys       []string
257	PublicKeyAuths []string
258}
259
260func init() {
261	if fips140.Enabled() {
262		defaultHostKeyAlgos = slices.DeleteFunc(defaultHostKeyAlgos, func(algo string) bool {
263			_, err := hashFunc(underlyingAlgo(algo))
264			return err != nil
265		})
266		defaultPubKeyAuthAlgos = slices.DeleteFunc(defaultPubKeyAuthAlgos, func(algo string) bool {
267			_, err := hashFunc(underlyingAlgo(algo))
268			return err != nil
269		})
270	}
271}
272
273func hashFunc(format string) (crypto.Hash, error) {
274	switch format {
275	case KeyAlgoRSASHA256, KeyAlgoECDSA256, KeyAlgoSKED25519, KeyAlgoSKECDSA256:
276		return crypto.SHA256, nil
277	case KeyAlgoECDSA384:
278		return crypto.SHA384, nil
279	case KeyAlgoRSASHA512, KeyAlgoECDSA521:
280		return crypto.SHA512, nil
281	case KeyAlgoED25519:
282		// KeyAlgoED25519 doesn't pre-hash.
283		return 0, nil
284	case KeyAlgoRSA, InsecureKeyAlgoDSA:
285		if fips140.Enabled() {
286			return 0, fmt.Errorf("ssh: hash algorithm for format %q not allowed in FIPS 140 mode", format)
287		}
288		return crypto.SHA1, nil
289	default:
290		return 0, fmt.Errorf("ssh: hash algorithm for format %q not mapped", format)
291	}
292}
293
294// SupportedAlgorithms returns algorithms currently implemented by this package,
295// excluding those with security issues, which are returned by
296// InsecureAlgorithms. The algorithms listed here are in preference order.
297func SupportedAlgorithms() Algorithms {
298	return Algorithms{
299		Ciphers:        slices.Clone(supportedCiphers),
300		MACs:           slices.Clone(supportedMACs),
301		KeyExchanges:   slices.Clone(supportedKexAlgos),
302		HostKeys:       slices.Clone(supportedHostKeyAlgos),
303		PublicKeyAuths: slices.Clone(supportedPubKeyAuthAlgos),
304	}
305}
306
307// InsecureAlgorithms returns algorithms currently implemented by this package
308// and which have security issues.
309func InsecureAlgorithms() Algorithms {
310	return Algorithms{
311		KeyExchanges:   slices.Clone(insecureKexAlgos),
312		Ciphers:        slices.Clone(insecureCiphers),
313		MACs:           slices.Clone(insecureMACs),
314		HostKeys:       slices.Clone(insecureHostKeyAlgos),
315		PublicKeyAuths: slices.Clone(insecurePubKeyAuthAlgos),
316	}
317}
318
319var supportedCompressions = []string{compressionNone}
320
321// algorithmsForKeyFormat returns the supported signature algorithms for a given
322// public key format (PublicKey.Type), in order of preference. See RFC 8332,
323// Section 2. See also the note in sendKexInit on backwards compatibility.
324func algorithmsForKeyFormat(keyFormat string) []string {
325	switch keyFormat {
326	case KeyAlgoRSA:
327		return []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA}
328	case CertAlgoRSAv01:
329		return []string{CertAlgoRSASHA256v01, CertAlgoRSASHA512v01, CertAlgoRSAv01}
330	default:
331		return []string{keyFormat}
332	}
333}
334
335// keyFormatForAlgorithm returns the key format corresponding to the given
336// signature algorithm. It returns an empty string if the signature algorithm is
337// invalid or unsupported.
338func keyFormatForAlgorithm(sigAlgo string) string {
339	switch sigAlgo {
340	case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512:
341		return KeyAlgoRSA
342	case CertAlgoRSAv01, CertAlgoRSASHA256v01, CertAlgoRSASHA512v01:
343		return CertAlgoRSAv01
344	case KeyAlgoED25519,
345		KeyAlgoSKED25519,
346		KeyAlgoSKECDSA256,
347		KeyAlgoECDSA256,
348		KeyAlgoECDSA384,
349		KeyAlgoECDSA521,
350		InsecureKeyAlgoDSA,
351		InsecureCertAlgoDSAv01,
352		CertAlgoECDSA256v01,
353		CertAlgoECDSA384v01,
354		CertAlgoECDSA521v01,
355		CertAlgoSKECDSA256v01,
356		CertAlgoED25519v01,
357		CertAlgoSKED25519v01:
358		return sigAlgo
359	default:
360		return ""
361	}
362}
363
364// isRSA returns whether algo is a supported RSA algorithm, including certificate
365// algorithms.
366func isRSA(algo string) bool {
367	algos := algorithmsForKeyFormat(KeyAlgoRSA)
368	return slices.Contains(algos, underlyingAlgo(algo))
369}
370
371func isRSACert(algo string) bool {
372	_, ok := certKeyAlgoNames[algo]
373	if !ok {
374		return false
375	}
376	return isRSA(algo)
377}
378
379// unexpectedMessageError results when the SSH message that we received didn't
380// match what we wanted.
381func unexpectedMessageError(expected, got uint8) error {
382	return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
383}
384
385// parseError results from a malformed SSH message.
386func parseError(tag uint8) error {
387	return fmt.Errorf("ssh: parse error in message type %d", tag)
388}
389
390func findCommon(what string, client []string, server []string, isClient bool) (string, error) {
391	for _, c := range client {
392		for _, s := range server {
393			if c == s {
394				return c, nil
395			}
396		}
397	}
398	err := &AlgorithmNegotiationError{
399		What: what,
400	}
401	if isClient {
402		err.SupportedAlgorithms = client
403		err.RequestedAlgorithms = server
404	} else {
405		err.SupportedAlgorithms = server
406		err.RequestedAlgorithms = client
407	}
408	return "", err
409}
410
411// AlgorithmNegotiationError defines the error returned if the client and the
412// server cannot agree on an algorithm for key exchange, host key, cipher, MAC.
413type AlgorithmNegotiationError struct {
414	What string
415	// RequestedAlgorithms lists the algorithms supported by the peer.
416	RequestedAlgorithms []string
417	// SupportedAlgorithms lists the algorithms supported on our side.
418	SupportedAlgorithms []string
419}
420
421func (a *AlgorithmNegotiationError) Error() string {
422	return fmt.Sprintf("ssh: no common algorithm for %s; we offered: %v, peer offered: %v",
423		a.What, a.SupportedAlgorithms, a.RequestedAlgorithms)
424}
425
426// DirectionAlgorithms defines the algorithms negotiated in one direction
427// (either read or write).
428type DirectionAlgorithms struct {
429	Cipher      string
430	MAC         string
431	compression string
432}
433
434// rekeyBytes returns a rekeying intervals in bytes.
435func (a *DirectionAlgorithms) rekeyBytes() int64 {
436	// According to RFC 4344 block ciphers should rekey after
437	// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
438	// 128.
439	switch a.Cipher {
440	case CipherAES128CTR, CipherAES192CTR, CipherAES256CTR, CipherAES128GCM, CipherAES256GCM, InsecureCipherAES128CBC:
441		return 16 * (1 << 32)
442
443	}
444
445	// For others, stick with RFC 4253 recommendation to rekey after 1 Gb of data.
446	return 1 << 30
447}
448
449var aeadCiphers = map[string]bool{
450	CipherAES128GCM:        true,
451	CipherAES256GCM:        true,
452	CipherChaCha20Poly1305: true,
453}
454
455func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *NegotiatedAlgorithms, err error) {
456	result := &NegotiatedAlgorithms{}
457
458	result.KeyExchange, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos, isClient)
459	if err != nil {
460		return
461	}
462
463	result.HostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos, isClient)
464	if err != nil {
465		return
466	}
467
468	stoc, ctos := &result.Write, &result.Read
469	if isClient {
470		ctos, stoc = stoc, ctos
471	}
472
473	ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer, isClient)
474	if err != nil {
475		return
476	}
477
478	stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient, isClient)
479	if err != nil {
480		return
481	}
482
483	if !aeadCiphers[ctos.Cipher] {
484		ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer, isClient)
485		if err != nil {
486			return
487		}
488	}
489
490	if !aeadCiphers[stoc.Cipher] {
491		stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient, isClient)
492		if err != nil {
493			return
494		}
495	}
496
497	ctos.compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer, isClient)
498	if err != nil {
499		return
500	}
501
502	stoc.compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient, isClient)
503	if err != nil {
504		return
505	}
506
507	return result, nil
508}
509
510// If rekeythreshold is too small, we can't make any progress sending
511// stuff.
512const minRekeyThreshold uint64 = 256
513
514// Config contains configuration data common to both ServerConfig and
515// ClientConfig.
516type Config struct {
517	// Rand provides the source of entropy for cryptographic
518	// primitives. If Rand is nil, the cryptographic random reader
519	// in package crypto/rand will be used.
520	Rand io.Reader
521
522	// The maximum number of bytes sent or received after which a
523	// new key is negotiated. It must be at least 256. If
524	// unspecified, a size suitable for the chosen cipher is used.
525	RekeyThreshold uint64
526
527	// The allowed key exchanges algorithms. If unspecified then a default set
528	// of algorithms is used. Unsupported values are silently ignored.
529	KeyExchanges []string
530
531	// The allowed cipher algorithms. If unspecified then a sensible default is
532	// used. Unsupported values are silently ignored.
533	Ciphers []string
534
535	// The allowed MAC algorithms. If unspecified then a sensible default is
536	// used. Unsupported values are silently ignored.
537	MACs []string
538}
539
540// SetDefaults sets sensible values for unset fields in config. This is
541// exported for testing: Configs passed to SSH functions are copied and have
542// default values set automatically.
543func (c *Config) SetDefaults() {
544	if c.Rand == nil {
545		c.Rand = rand.Reader
546	}
547	if c.Ciphers == nil {
548		c.Ciphers = defaultCiphers
549	}
550	var ciphers []string
551	for _, c := range c.Ciphers {
552		if cipherModes[c] != nil {
553			// Ignore the cipher if we have no cipherModes definition.
554			ciphers = append(ciphers, c)
555		}
556	}
557	c.Ciphers = ciphers
558
559	if c.KeyExchanges == nil {
560		c.KeyExchanges = defaultKexAlgos
561	}
562	var kexs []string
563	for _, k := range c.KeyExchanges {
564		if kexAlgoMap[k] != nil {
565			// Ignore the KEX if we have no kexAlgoMap definition.
566			kexs = append(kexs, k)
567			if k == KeyExchangeCurve25519 && !slices.Contains(c.KeyExchanges, keyExchangeCurve25519LibSSH) {
568				kexs = append(kexs, keyExchangeCurve25519LibSSH)
569			}
570		}
571	}
572	c.KeyExchanges = kexs
573
574	if c.MACs == nil {
575		c.MACs = defaultMACs
576	}
577	var macs []string
578	for _, m := range c.MACs {
579		if macModes[m] != nil {
580			// Ignore the MAC if we have no macModes definition.
581			macs = append(macs, m)
582		}
583	}
584	c.MACs = macs
585
586	if c.RekeyThreshold == 0 {
587		// cipher specific default
588	} else if c.RekeyThreshold < minRekeyThreshold {
589		c.RekeyThreshold = minRekeyThreshold
590	} else if c.RekeyThreshold >= math.MaxInt64 {
591		// Avoid weirdness if somebody uses -1 as a threshold.
592		c.RekeyThreshold = math.MaxInt64
593	}
594}
595
596// buildDataSignedForAuth returns the data that is signed in order to prove
597// possession of a private key. See RFC 4252, section 7. algo is the advertised
598// algorithm, and may be a certificate type.
599func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo string, pubKey []byte) []byte {
600	data := struct {
601		Session []byte
602		Type    byte
603		User    string
604		Service string
605		Method  string
606		Sign    bool
607		Algo    string
608		PubKey  []byte
609	}{
610		sessionID,
611		msgUserAuthRequest,
612		req.User,
613		req.Service,
614		req.Method,
615		true,
616		algo,
617		pubKey,
618	}
619	return Marshal(data)
620}
621
622func appendU16(buf []byte, n uint16) []byte {
623	return append(buf, byte(n>>8), byte(n))
624}
625
626func appendU32(buf []byte, n uint32) []byte {
627	return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
628}
629
630func appendU64(buf []byte, n uint64) []byte {
631	return append(buf,
632		byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
633		byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
634}
635
636func appendInt(buf []byte, n int) []byte {
637	return appendU32(buf, uint32(n))
638}
639
640func appendString(buf []byte, s string) []byte {
641	buf = appendU32(buf, uint32(len(s)))
642	buf = append(buf, s...)
643	return buf
644}
645
646func appendBool(buf []byte, b bool) []byte {
647	if b {
648		return append(buf, 1)
649	}
650	return append(buf, 0)
651}
652
653// newCond is a helper to hide the fact that there is no usable zero
654// value for sync.Cond.
655func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
656
657// window represents the buffer available to clients
658// wishing to write to a channel.
659type window struct {
660	*sync.Cond
661	win          uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
662	writeWaiters int
663	closed       bool
664}
665
666// add adds win to the amount of window available
667// for consumers.
668func (w *window) add(win uint32) bool {
669	// a zero sized window adjust is a noop.
670	if win == 0 {
671		return true
672	}
673	w.L.Lock()
674	if w.win+win < win {
675		w.L.Unlock()
676		return false
677	}
678	w.win += win
679	// It is unusual that multiple goroutines would be attempting to reserve
680	// window space, but not guaranteed. Use broadcast to notify all waiters
681	// that additional window is available.
682	w.Broadcast()
683	w.L.Unlock()
684	return true
685}
686
687// close sets the window to closed, so all reservations fail
688// immediately.
689func (w *window) close() {
690	w.L.Lock()
691	w.closed = true
692	w.Broadcast()
693	w.L.Unlock()
694}
695
696// reserve reserves win from the available window capacity.
697// If no capacity remains, reserve will block. reserve may
698// return less than requested.
699func (w *window) reserve(win uint32) (uint32, error) {
700	var err error
701	w.L.Lock()
702	w.writeWaiters++
703	w.Broadcast()
704	for w.win == 0 && !w.closed {
705		w.Wait()
706	}
707	w.writeWaiters--
708	if w.win < win {
709		win = w.win
710	}
711	w.win -= win
712	if w.closed {
713		err = io.EOF
714	}
715	w.L.Unlock()
716	return win, err
717}
718
719// waitWriterBlocked waits until some goroutine is blocked for further
720// writes. It is used in tests only.
721func (w *window) waitWriterBlocked() {
722	w.Cond.L.Lock()
723	for w.writeWaiters == 0 {
724		w.Cond.Wait()
725	}
726	w.Cond.L.Unlock()
727}