Source File
handshake.go
Belonging Package
golang.org/x/crypto/ssh
package ssh
import (
)
const debugHandshake = false
const chanSize = 16
type keyingTransport interface {
packetConn
prepareKeyChange(*algorithms, *kexResult) error
}
type handshakeTransport struct {
conn keyingTransport
config *Config
serverVersion []byte
clientVersion []byte
incoming chan []byte
readError error
mu sync.Mutex
writeError error
sentInitPacket []byte
sentInitMsg *kexInitMsg
pendingPackets [][]byte // Used when a key exchange is in progress.
requestKex chan struct{}
startKex chan *pendingKex
sessionID []byte
}
type pendingKex struct {
otherInit []byte
done chan error
}
func ( keyingTransport, *Config, , []byte) *handshakeTransport {
:= &handshakeTransport{
conn: ,
serverVersion: ,
clientVersion: ,
incoming: make(chan []byte, chanSize),
requestKex: make(chan struct{}, 1),
startKex: make(chan *pendingKex, 1),
config: ,
}
.resetReadThresholds()
.resetWriteThresholds()
.requestKex <- struct{}{}
return
}
func ( keyingTransport, , []byte, *ClientConfig, string, net.Addr) *handshakeTransport {
:= newHandshakeTransport(, &.Config, , )
.dialAddress =
.remoteAddr =
.hostKeyCallback = .HostKeyCallback
.bannerCallback = .BannerCallback
if .HostKeyAlgorithms != nil {
.hostKeyAlgorithms = .HostKeyAlgorithms
} else {
.hostKeyAlgorithms = supportedHostKeyAlgos
}
go .readLoop()
go .kexLoop()
return
}
func ( keyingTransport, , []byte, *ServerConfig) *handshakeTransport {
:= newHandshakeTransport(, &.Config, , )
.hostKeys = .hostKeys
go .readLoop()
go .kexLoop()
return
}
func ( *handshakeTransport) () []byte {
return .sessionID
}
func ( *handshakeTransport) () error {
, := .readPacket()
if != nil {
return
}
if [0] != msgNewKeys {
return fmt.Errorf("ssh: first packet should be msgNewKeys")
}
return nil
}
func ( *handshakeTransport) () string {
if len(.hostKeys) > 0 {
return "server"
}
return "client"
}
func ( *handshakeTransport) ( []byte, bool) {
:= "got"
if {
= "sent"
}
if [0] == msgChannelData || [0] == msgChannelExtendedData {
log.Printf("%s %s data (packet %d bytes)", .id(), , len())
} else {
, := decode()
log.Printf("%s %s %T %v (%v)", .id(), , , , )
}
}
func ( *handshakeTransport) () ([]byte, error) {
, := <-.incoming
if ! {
return nil, .readError
}
return , nil
}
func ( *handshakeTransport) () {
:= true
for {
, := .readOnePacket()
= false
if != nil {
.readError =
close(.incoming)
break
}
if [0] == msgIgnore || [0] == msgDebug {
continue
}
.incoming <-
}
}
func ( *handshakeTransport) ( []byte) error {
if debugHandshake {
.printPacket(, true)
}
return .conn.writePacket()
}
func ( *handshakeTransport) () error {
.mu.Lock()
defer .mu.Unlock()
return .writeError
}
func ( *handshakeTransport) ( error) {
.mu.Lock()
defer .mu.Unlock()
if .writeError == nil && != nil {
.writeError =
}
}
func ( *handshakeTransport) () {
select {
case .requestKex <- struct{}{}:
}
}
func ( *handshakeTransport) () {
.writePacketsLeft = packetRekeyThreshold
if .config.RekeyThreshold > 0 {
.writeBytesLeft = int64(.config.RekeyThreshold)
} else if .algorithms != nil {
.writeBytesLeft = .algorithms.w.rekeyBytes()
} else {
.writeBytesLeft = 1 << 30
}
}
func ( *handshakeTransport) () {
:
for .getWriteError() == nil {
var *pendingKex
var bool
for == nil || ! {
var bool
select {
case , = <-.startKex:
if ! {
break
}
case <-.requestKex:
break
}
if ! {
if := .sendKexInit(); != nil {
.recordWriteError()
break
}
= true
}
}
if := .getWriteError(); != nil {
if != nil {
.done <-
}
break
}
:= .enterKeyExchange(.otherInit)
.mu.Lock()
.writeError =
.sentInitPacket = nil
.sentInitMsg = nil
.resetWriteThresholds()
:
for {
select {
default:
break
}
}
.done <- .writeError
for , := range .pendingPackets {
.writeError = .pushPacket()
if .writeError != nil {
break
}
}
.pendingPackets = .pendingPackets[:0]
.mu.Unlock()
}
go func() {
for := range .startKex {
.done <- .writeError
}
}()
const packetRekeyThreshold = (1 << 31)
func ( *handshakeTransport) () {
.readPacketsLeft = packetRekeyThreshold
if .config.RekeyThreshold > 0 {
.readBytesLeft = int64(.config.RekeyThreshold)
} else if .algorithms != nil {
.readBytesLeft = .algorithms.r.rekeyBytes()
} else {
.readBytesLeft = 1 << 30
}
}
func ( *handshakeTransport) ( bool) ([]byte, error) {
, := .conn.readPacket()
if != nil {
return nil,
}
if .readPacketsLeft > 0 {
.readPacketsLeft--
} else {
.requestKeyExchange()
}
if .readBytesLeft > 0 {
.readBytesLeft -= int64(len())
} else {
.requestKeyExchange()
}
if debugHandshake {
.printPacket(, false)
}
if && [0] != msgKexInit {
return nil, fmt.Errorf("ssh: first packet should be msgKexInit")
}
if [0] != msgKexInit {
return , nil
}
:= .sessionID == nil
:= pendingKex{
done: make(chan error, 1),
otherInit: ,
}
.startKex <- &
= <-.done
if debugHandshake {
log.Printf("%s exited key exchange (first %v), err %v", .id(), , )
}
if != nil {
return nil,
}
.resetReadThresholds()
= []byte{msgNewKeys}
}
return , nil
}
return nil
}
:= &kexInitMsg{
KexAlgos: .config.KeyExchanges,
CiphersClientServer: .config.Ciphers,
CiphersServerClient: .config.Ciphers,
MACsClientServer: .config.MACs,
MACsServerClient: .config.MACs,
CompressionClientServer: supportedCompressions,
CompressionServerClient: supportedCompressions,
}
io.ReadFull(rand.Reader, .Cookie[:])
if len(.hostKeys) > 0 {
for , := range .hostKeys {
.ServerHostKeyAlgos = append(
.ServerHostKeyAlgos, .PublicKey().Type())
}
} else {
.ServerHostKeyAlgos = .hostKeyAlgorithms
}
:= Marshal()
:= make([]byte, len())
copy(, )
if := .pushPacket(); != nil {
return
}
.sentInitMsg =
.sentInitPacket =
return nil
}
func ( *handshakeTransport) ( []byte) error {
switch [0] {
case msgKexInit:
return errors.New("ssh: only handshakeTransport can send kexInit")
case msgNewKeys:
return errors.New("ssh: only handshakeTransport can send newKeys")
}
.mu.Lock()
defer .mu.Unlock()
if .writeError != nil {
return .writeError
}
:= make([]byte, len())
copy(, )
.pendingPackets = append(.pendingPackets, )
return nil
}
if .writeBytesLeft > 0 {
.writeBytesLeft -= int64(len())
} else {
.requestKeyExchange()
}
if .writePacketsLeft > 0 {
.writePacketsLeft--
} else {
.requestKeyExchange()
}
if := .pushPacket(); != nil {
.writeError =
}
return nil
}
func ( *handshakeTransport) () error {
return .conn.Close()
}
func ( *handshakeTransport) ( []byte) error {
if debugHandshake {
log.Printf("%s entered key exchange", .id())
}
:= &kexInitMsg{}
if := Unmarshal(, ); != nil {
return
}
:= handshakeMagics{
clientVersion: .clientVersion,
serverVersion: .serverVersion,
clientKexInit: ,
serverKexInit: .sentInitPacket,
}
:=
:= .sentInitMsg
:= len(.hostKeys) == 0
if {
, = ,
.clientKexInit = .sentInitPacket
.serverKexInit =
}
var error
.algorithms, = findAgreedAlgorithms(, , )
if != nil {
return
}
if , := .conn.readPacket(); != nil {
return
}
}
, := kexAlgoMap[.algorithms.kex]
if ! {
return fmt.Errorf("ssh: unexpected key exchange algorithm %v", .algorithms.kex)
}
var *kexResult
if len(.hostKeys) > 0 {
, = .server(, .algorithms, &)
} else {
, = .client(, .algorithms, &)
}
if != nil {
return
}
if .sessionID == nil {
.sessionID = .H
}
.SessionID = .sessionID
if := .conn.prepareKeyChange(.algorithms, ); != nil {
return
}
if = .conn.writePacket([]byte{msgNewKeys}); != nil {
return
}
if , := .conn.readPacket(); != nil {
return
} else if [0] != msgNewKeys {
return unexpectedMessageError(msgNewKeys, [0])
}
return nil
}
func ( *handshakeTransport) ( kexAlgorithm, *algorithms, *handshakeMagics) (*kexResult, error) {
var Signer
for , := range .hostKeys {
if .hostKey == .PublicKey().Type() {
=
}
}
, := .Server(.conn, .config.Rand, , )
return ,
}
func ( *handshakeTransport) ( kexAlgorithm, *algorithms, *handshakeMagics) (*kexResult, error) {
, := .Client(.conn, .config.Rand, )
if != nil {
return nil,
}
, := ParsePublicKey(.HostKey)
if != nil {
return nil,
}
if := verifyHostKeySignature(, ); != nil {
return nil,
}
= .hostKeyCallback(.dialAddress, .remoteAddr, )
if != nil {
return nil,
}
return , nil
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin 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. |