package ssh
import (
"crypto"
"crypto/rand"
"fmt"
"io"
"math"
"strings"
"sync"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
)
const (
compressionNone = "none"
serviceUserAuth = "ssh-userauth"
serviceSSH = "ssh-connection"
)
var supportedCiphers = []string {
"aes128-ctr" , "aes192-ctr" , "aes256-ctr" ,
"aes128-gcm@openssh.com" , gcm256CipherID ,
chacha20Poly1305ID ,
"arcfour256" , "arcfour128" , "arcfour" ,
aes128cbcID ,
tripledescbcID ,
}
var preferredCiphers = []string {
"aes128-gcm@openssh.com" , gcm256CipherID ,
chacha20Poly1305ID ,
"aes128-ctr" , "aes192-ctr" , "aes256-ctr" ,
}
var supportedKexAlgos = []string {
kexAlgoCurve25519SHA256 , kexAlgoCurve25519SHA256LibSSH ,
kexAlgoECDH256 , kexAlgoECDH384 , kexAlgoECDH521 ,
kexAlgoDH14SHA256 , kexAlgoDH16SHA512 , kexAlgoDH14SHA1 ,
kexAlgoDH1SHA1 ,
}
var serverForbiddenKexAlgos = map [string ]struct {}{
kexAlgoDHGEXSHA1 : {},
kexAlgoDHGEXSHA256 : {},
}
var preferredKexAlgos = []string {
kexAlgoCurve25519SHA256 , kexAlgoCurve25519SHA256LibSSH ,
kexAlgoECDH256 , kexAlgoECDH384 , kexAlgoECDH521 ,
kexAlgoDH14SHA256 , kexAlgoDH14SHA1 ,
}
var supportedHostKeyAlgos = []string {
CertAlgoRSASHA256v01 , CertAlgoRSASHA512v01 ,
CertAlgoRSAv01 , CertAlgoDSAv01 , CertAlgoECDSA256v01 ,
CertAlgoECDSA384v01 , CertAlgoECDSA521v01 , CertAlgoED25519v01 ,
KeyAlgoECDSA256 , KeyAlgoECDSA384 , KeyAlgoECDSA521 ,
KeyAlgoRSASHA256 , KeyAlgoRSASHA512 ,
KeyAlgoRSA , KeyAlgoDSA ,
KeyAlgoED25519 ,
}
var supportedMACs = []string {
"hmac-sha2-256-etm@openssh.com" , "hmac-sha2-512-etm@openssh.com" , "hmac-sha2-256" , "hmac-sha2-512" , "hmac-sha1" , "hmac-sha1-96" ,
}
var supportedCompressions = []string {compressionNone }
var hashFuncs = map [string ]crypto .Hash {
KeyAlgoRSA : crypto .SHA1 ,
KeyAlgoRSASHA256 : crypto .SHA256 ,
KeyAlgoRSASHA512 : crypto .SHA512 ,
KeyAlgoDSA : crypto .SHA1 ,
KeyAlgoECDSA256 : crypto .SHA256 ,
KeyAlgoECDSA384 : crypto .SHA384 ,
KeyAlgoECDSA521 : crypto .SHA512 ,
KeyAlgoSKECDSA256 : crypto .SHA256 ,
KeyAlgoSKED25519 : crypto .SHA256 ,
}
func algorithmsForKeyFormat(keyFormat string ) []string {
switch keyFormat {
case KeyAlgoRSA :
return []string {KeyAlgoRSASHA256 , KeyAlgoRSASHA512 , KeyAlgoRSA }
case CertAlgoRSAv01 :
return []string {CertAlgoRSASHA256v01 , CertAlgoRSASHA512v01 , CertAlgoRSAv01 }
default :
return []string {keyFormat }
}
}
func isRSA(algo string ) bool {
algos := algorithmsForKeyFormat (KeyAlgoRSA )
return contains (algos , underlyingAlgo (algo ))
}
var supportedPubKeyAuthAlgos = []string {
KeyAlgoED25519 ,
KeyAlgoSKED25519 , KeyAlgoSKECDSA256 ,
KeyAlgoECDSA256 , KeyAlgoECDSA384 , KeyAlgoECDSA521 ,
KeyAlgoRSASHA256 , KeyAlgoRSASHA512 , KeyAlgoRSA ,
KeyAlgoDSA ,
}
var supportedPubKeyAuthAlgosList = strings .Join (supportedPubKeyAuthAlgos , "," )
func unexpectedMessageError(expected , got uint8 ) error {
return fmt .Errorf ("ssh: unexpected message type %d (expected %d)" , got , expected )
}
func parseError(tag uint8 ) error {
return fmt .Errorf ("ssh: parse error in message type %d" , tag )
}
func findCommon(what string , client []string , server []string ) (common string , err error ) {
for _ , c := range client {
for _ , s := range server {
if c == s {
return c , nil
}
}
}
return "" , fmt .Errorf ("ssh: no common algorithm for %s; client offered: %v, server offered: %v" , what , client , server )
}
type directionAlgorithms struct {
Cipher string
MAC string
Compression string
}
func (a *directionAlgorithms ) rekeyBytes () int64 {
switch a .Cipher {
case "aes128-ctr" , "aes192-ctr" , "aes256-ctr" , gcm128CipherID , gcm256CipherID , aes128cbcID :
return 16 * (1 << 32 )
}
return 1 << 30
}
var aeadCiphers = map [string ]bool {
gcm128CipherID : true ,
gcm256CipherID : true ,
chacha20Poly1305ID : true ,
}
type algorithms struct {
kex string
hostKey string
w directionAlgorithms
r directionAlgorithms
}
func findAgreedAlgorithms(isClient bool , clientKexInit , serverKexInit *kexInitMsg ) (algs *algorithms , err error ) {
result := &algorithms {}
result .kex , err = findCommon ("key exchange" , clientKexInit .KexAlgos , serverKexInit .KexAlgos )
if err != nil {
return
}
result .hostKey , err = findCommon ("host key" , clientKexInit .ServerHostKeyAlgos , serverKexInit .ServerHostKeyAlgos )
if err != nil {
return
}
stoc , ctos := &result .w , &result .r
if isClient {
ctos , stoc = stoc , ctos
}
ctos .Cipher , err = findCommon ("client to server cipher" , clientKexInit .CiphersClientServer , serverKexInit .CiphersClientServer )
if err != nil {
return
}
stoc .Cipher , err = findCommon ("server to client cipher" , clientKexInit .CiphersServerClient , serverKexInit .CiphersServerClient )
if err != nil {
return
}
if !aeadCiphers [ctos .Cipher ] {
ctos .MAC , err = findCommon ("client to server MAC" , clientKexInit .MACsClientServer , serverKexInit .MACsClientServer )
if err != nil {
return
}
}
if !aeadCiphers [stoc .Cipher ] {
stoc .MAC , err = findCommon ("server to client MAC" , clientKexInit .MACsServerClient , serverKexInit .MACsServerClient )
if err != nil {
return
}
}
ctos .Compression , err = findCommon ("client to server compression" , clientKexInit .CompressionClientServer , serverKexInit .CompressionClientServer )
if err != nil {
return
}
stoc .Compression , err = findCommon ("server to client compression" , clientKexInit .CompressionServerClient , serverKexInit .CompressionServerClient )
if err != nil {
return
}
return result , nil
}
const minRekeyThreshold uint64 = 256
type Config struct {
Rand io .Reader
RekeyThreshold uint64
KeyExchanges []string
Ciphers []string
MACs []string
}
func (c *Config ) SetDefaults () {
if c .Rand == nil {
c .Rand = rand .Reader
}
if c .Ciphers == nil {
c .Ciphers = preferredCiphers
}
var ciphers []string
for _ , c := range c .Ciphers {
if cipherModes [c ] != nil {
ciphers = append (ciphers , c )
}
}
c .Ciphers = ciphers
if c .KeyExchanges == nil {
c .KeyExchanges = preferredKexAlgos
}
var kexs []string
for _ , k := range c .KeyExchanges {
if kexAlgoMap [k ] != nil {
kexs = append (kexs , k )
}
}
c .KeyExchanges = kexs
if c .MACs == nil {
c .MACs = supportedMACs
}
var macs []string
for _ , m := range c .MACs {
if macModes [m ] != nil {
macs = append (macs , m )
}
}
c .MACs = macs
if c .RekeyThreshold == 0 {
} else if c .RekeyThreshold < minRekeyThreshold {
c .RekeyThreshold = minRekeyThreshold
} else if c .RekeyThreshold >= math .MaxInt64 {
c .RekeyThreshold = math .MaxInt64
}
}
func buildDataSignedForAuth(sessionID []byte , req userAuthRequestMsg , algo string , pubKey []byte ) []byte {
data := struct {
Session []byte
Type byte
User string
Service string
Method string
Sign bool
Algo string
PubKey []byte
}{
sessionID ,
msgUserAuthRequest ,
req .User ,
req .Service ,
req .Method ,
true ,
algo ,
pubKey ,
}
return Marshal (data )
}
func appendU16(buf []byte , n uint16 ) []byte {
return append (buf , byte (n >>8 ), byte (n ))
}
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 ))
}
func appendString(buf []byte , s string ) []byte {
buf = appendU32 (buf , uint32 (len (s )))
buf = append (buf , s ...)
return buf
}
func appendBool(buf []byte , b bool ) []byte {
if b {
return append (buf , 1 )
}
return append (buf , 0 )
}
func newCond() *sync .Cond { return sync .NewCond (new (sync .Mutex )) }
type window struct {
*sync .Cond
win uint32
writeWaiters int
closed bool
}
func (w *window ) add (win uint32 ) bool {
if win == 0 {
return true
}
w .L .Lock ()
if w .win +win < win {
w .L .Unlock ()
return false
}
w .win += win
w .Broadcast ()
w .L .Unlock ()
return true
}
func (w *window ) close () {
w .L .Lock ()
w .closed = true
w .Broadcast ()
w .L .Unlock ()
}
func (w *window ) reserve (win uint32 ) (uint32 , error ) {
var err error
w .L .Lock ()
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 , err
}
func (w *window ) waitWriterBlocked () {
w .Cond .L .Lock ()
for w .writeWaiters == 0 {
w .Cond .Wait ()
}
w .Cond .L .Unlock ()
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .