Skip to content

Commit e944286

Browse files
drakkangopherbot
authored andcommitted
ssh: expose negotiated algorithms
Fixes golang/go#58523 Fixes golang/go#46638 Change-Id: Ic64bd2fdd6e9ec96acac3ed4be842e2fbb15231d Reviewed-on: https://go-review.googlesource.com/c/crypto/+/538235 Reviewed-by: Filippo Valsorda <filippo@golang.org> Auto-Submit: Nicola Murino <nicola.murino@gmail.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent 78a1fd7 commit e944286

File tree

10 files changed

+139
-64
lines changed

10 files changed

+139
-64
lines changed

ssh/cipher.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ func newRC4(key, iv []byte) (cipher.Stream, error) {
5858
type cipherMode struct {
5959
keySize int
6060
ivSize int
61-
create func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error)
61+
create func(key, iv []byte, macKey []byte, algs DirectionAlgorithms) (packetCipher, error)
6262
}
6363

64-
func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
65-
return func(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
64+
func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
65+
return func(key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
6666
stream, err := createFunc(key, iv)
6767
if err != nil {
6868
return nil, err
@@ -307,7 +307,7 @@ type gcmCipher struct {
307307
buf []byte
308308
}
309309

310-
func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
310+
func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs DirectionAlgorithms) (packetCipher, error) {
311311
c, err := aes.NewCipher(key)
312312
if err != nil {
313313
return nil, err
@@ -429,7 +429,7 @@ type cbcCipher struct {
429429
oracleCamouflage uint32
430430
}
431431

432-
func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
432+
func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
433433
cbc := &cbcCipher{
434434
mac: macModes[algs.MAC].new(macKey),
435435
decrypter: cipher.NewCBCDecrypter(c, iv),
@@ -443,7 +443,7 @@ func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorith
443443
return cbc, nil
444444
}
445445

446-
func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
446+
func newAESCBCCipher(key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
447447
c, err := aes.NewCipher(key)
448448
if err != nil {
449449
return nil, err
@@ -457,7 +457,7 @@ func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCi
457457
return cbc, nil
458458
}
459459

460-
func newTripleDESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
460+
func newTripleDESCBCCipher(key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
461461
c, err := des.NewTripleDESCipher(key)
462462
if err != nil {
463463
return nil, err
@@ -648,7 +648,7 @@ type chacha20Poly1305Cipher struct {
648648
buf []byte
649649
}
650650

651-
func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
651+
func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs DirectionAlgorithms) (packetCipher, error) {
652652
if len(key) != 64 {
653653
panic(len(key))
654654
}

ssh/cipher_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ func TestPacketCiphers(t *testing.T) {
4444

4545
func testPacketCipher(t *testing.T, cipher, mac string) {
4646
kr := &kexResult{Hash: crypto.SHA1}
47-
algs := directionAlgorithms{
47+
algs := DirectionAlgorithms{
4848
Cipher: cipher,
4949
MAC: mac,
50-
Compression: compressionNone,
50+
compression: compressionNone,
5151
}
5252
client, err := newPacketCipher(clientKeys, algs, kr)
5353
if err != nil {
@@ -77,10 +77,10 @@ func testPacketCipher(t *testing.T, cipher, mac string) {
7777

7878
func TestCBCOracleCounterMeasure(t *testing.T) {
7979
kr := &kexResult{Hash: crypto.SHA1}
80-
algs := directionAlgorithms{
80+
algs := DirectionAlgorithms{
8181
Cipher: InsecureCipherAES128CBC,
8282
MAC: HMACSHA1,
83-
Compression: compressionNone,
83+
compression: compressionNone,
8484
}
8585
client, err := newPacketCipher(clientKeys, algs, kr)
8686
if err != nil {
@@ -204,10 +204,10 @@ func TestCVE202143565(t *testing.T) {
204204
mac := HMACSHA256
205205

206206
kr := &kexResult{Hash: crypto.SHA1}
207-
algs := directionAlgorithms{
207+
algs := DirectionAlgorithms{
208208
Cipher: tc.cipher,
209209
MAC: mac,
210-
Compression: compressionNone,
210+
compression: compressionNone,
211211
}
212212
client, err := newPacketCipher(clientKeys, algs, kr)
213213
if err != nil {

ssh/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e
110110
}
111111

112112
c.sessionID = c.transport.getSessionID()
113+
c.algorithms = c.transport.getAlgorithms()
113114
return c.clientAuthenticate(config)
114115
}
115116

ssh/common.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,14 @@ var (
236236
}
237237
)
238238

239+
// NegotiatedAlgorithms defines algorithms negotiated between client and server.
240+
type NegotiatedAlgorithms struct {
241+
KeyExchange string
242+
HostKey string
243+
Read DirectionAlgorithms
244+
Write DirectionAlgorithms
245+
}
246+
239247
// Algorithms defines a set of algorithms that can be configured in the client
240248
// or server config for negotiation during a handshake.
241249
type Algorithms struct {
@@ -346,15 +354,16 @@ func findCommon(what string, client []string, server []string) (common string, e
346354
return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
347355
}
348356

349-
// directionAlgorithms records algorithm choices in one direction (either read or write)
350-
type directionAlgorithms struct {
357+
// DirectionAlgorithms defines the algorithms negotiated in one direction
358+
// (either read or write).
359+
type DirectionAlgorithms struct {
351360
Cipher string
352361
MAC string
353-
Compression string
362+
compression string
354363
}
355364

356365
// rekeyBytes returns a rekeying intervals in bytes.
357-
func (a *directionAlgorithms) rekeyBytes() int64 {
366+
func (a *DirectionAlgorithms) rekeyBytes() int64 {
358367
// According to RFC 4344 block ciphers should rekey after
359368
// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
360369
// 128.
@@ -374,27 +383,20 @@ var aeadCiphers = map[string]bool{
374383
CipherChaCha20Poly1305: true,
375384
}
376385

377-
type algorithms struct {
378-
kex string
379-
hostKey string
380-
w directionAlgorithms
381-
r directionAlgorithms
382-
}
383-
384-
func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
385-
result := &algorithms{}
386+
func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *NegotiatedAlgorithms, err error) {
387+
result := &NegotiatedAlgorithms{}
386388

387-
result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
389+
result.KeyExchange, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
388390
if err != nil {
389391
return
390392
}
391393

392-
result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
394+
result.HostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
393395
if err != nil {
394396
return
395397
}
396398

397-
stoc, ctos := &result.w, &result.r
399+
stoc, ctos := &result.Write, &result.Read
398400
if isClient {
399401
ctos, stoc = stoc, ctos
400402
}
@@ -423,12 +425,12 @@ func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMs
423425
}
424426
}
425427

426-
ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
428+
ctos.compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
427429
if err != nil {
428430
return
429431
}
430432

431-
stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
433+
stoc.compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
432434
if err != nil {
433435
return
434436
}

ssh/common_test.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,33 +51,33 @@ func TestFindAgreedAlgorithms(t *testing.T) {
5151
}
5252
}
5353

54-
initDirAlgs := func(a *directionAlgorithms) {
54+
initDirAlgs := func(a *DirectionAlgorithms) {
5555
if a.Cipher == "" {
5656
a.Cipher = "cipher1"
5757
}
5858
if a.MAC == "" {
5959
a.MAC = "mac1"
6060
}
61-
if a.Compression == "" {
62-
a.Compression = "compression1"
61+
if a.compression == "" {
62+
a.compression = "compression1"
6363
}
6464
}
6565

66-
initAlgs := func(a *algorithms) {
67-
if a.kex == "" {
68-
a.kex = "kex1"
66+
initAlgs := func(a *NegotiatedAlgorithms) {
67+
if a.KeyExchange == "" {
68+
a.KeyExchange = "kex1"
6969
}
70-
if a.hostKey == "" {
71-
a.hostKey = "hostkey1"
70+
if a.HostKey == "" {
71+
a.HostKey = "hostkey1"
7272
}
73-
initDirAlgs(&a.r)
74-
initDirAlgs(&a.w)
73+
initDirAlgs(&a.Read)
74+
initDirAlgs(&a.Write)
7575
}
7676

7777
type testcase struct {
7878
name string
7979
clientIn, serverIn kexInitMsg
80-
wantClient, wantServer algorithms
80+
wantClient, wantServer NegotiatedAlgorithms
8181
wantErr bool
8282
}
8383

@@ -120,19 +120,19 @@ func TestFindAgreedAlgorithms(t *testing.T) {
120120
CiphersClientServer: []string{"cipher2", "cipher1"},
121121
CiphersServerClient: []string{"cipher3", "cipher2"},
122122
},
123-
wantClient: algorithms{
124-
r: directionAlgorithms{
123+
wantClient: NegotiatedAlgorithms{
124+
Read: DirectionAlgorithms{
125125
Cipher: "cipher3",
126126
},
127-
w: directionAlgorithms{
127+
Write: DirectionAlgorithms{
128128
Cipher: "cipher2",
129129
},
130130
},
131-
wantServer: algorithms{
132-
w: directionAlgorithms{
131+
wantServer: NegotiatedAlgorithms{
132+
Write: DirectionAlgorithms{
133133
Cipher: "cipher3",
134134
},
135-
r: directionAlgorithms{
135+
Read: DirectionAlgorithms{
136136
Cipher: "cipher2",
137137
},
138138
},

ssh/connection.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ type Conn interface {
7474
// Disconnect
7575
}
7676

77+
// AlgorithmsConnMetadata is a ConnMetadata that can return the algorithms
78+
// negotiated between client and server.
79+
type AlgorithmsConnMetadata interface {
80+
ConnMetadata
81+
Algorithms() NegotiatedAlgorithms
82+
}
83+
7784
// DiscardRequests consumes and rejects all requests from the
7885
// passed-in channel.
7986
func DiscardRequests(in <-chan *Request) {
@@ -106,6 +113,7 @@ type sshConn struct {
106113
sessionID []byte
107114
clientVersion []byte
108115
serverVersion []byte
116+
algorithms NegotiatedAlgorithms
109117
}
110118

111119
func dup(src []byte) []byte {
@@ -141,3 +149,7 @@ func (c *sshConn) ClientVersion() []byte {
141149
func (c *sshConn) ServerVersion() []byte {
142150
return dup(c.serverVersion)
143151
}
152+
153+
func (c *sshConn) Algorithms() NegotiatedAlgorithms {
154+
return c.algorithms
155+
}

ssh/handshake.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type keyingTransport interface {
3838
// prepareKeyChange sets up a key change. The key change for a
3939
// direction will be effected if a msgNewKeys message is sent
4040
// or received.
41-
prepareKeyChange(*algorithms, *kexResult) error
41+
prepareKeyChange(*NegotiatedAlgorithms, *kexResult) error
4242

4343
// setStrictMode sets the strict KEX mode, notably triggering
4444
// sequence number resets on sending or receiving msgNewKeys.
@@ -115,7 +115,7 @@ type handshakeTransport struct {
115115
bannerCallback BannerCallback
116116

117117
// Algorithms agreed in the last key exchange.
118-
algorithms *algorithms
118+
algorithms *NegotiatedAlgorithms
119119

120120
// Counters exclusively owned by readLoop.
121121
readPacketsLeft uint32
@@ -184,6 +184,10 @@ func (t *handshakeTransport) getSessionID() []byte {
184184
return t.sessionID
185185
}
186186

187+
func (t *handshakeTransport) getAlgorithms() NegotiatedAlgorithms {
188+
return *t.algorithms
189+
}
190+
187191
// waitSession waits for the session to be established. This should be
188192
// the first thing to call after instantiating handshakeTransport.
189193
func (t *handshakeTransport) waitSession() error {
@@ -290,7 +294,7 @@ func (t *handshakeTransport) resetWriteThresholds() {
290294
if t.config.RekeyThreshold > 0 {
291295
t.writeBytesLeft = int64(t.config.RekeyThreshold)
292296
} else if t.algorithms != nil {
293-
t.writeBytesLeft = t.algorithms.w.rekeyBytes()
297+
t.writeBytesLeft = t.algorithms.Write.rekeyBytes()
294298
} else {
295299
t.writeBytesLeft = 1 << 30
296300
}
@@ -407,7 +411,7 @@ func (t *handshakeTransport) resetReadThresholds() {
407411
if t.config.RekeyThreshold > 0 {
408412
t.readBytesLeft = int64(t.config.RekeyThreshold)
409413
} else if t.algorithms != nil {
410-
t.readBytesLeft = t.algorithms.r.rekeyBytes()
414+
t.readBytesLeft = t.algorithms.Read.rekeyBytes()
411415
} else {
412416
t.readBytesLeft = 1 << 30
413417
}
@@ -700,9 +704,9 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
700704
}
701705
}
702706

703-
kex, ok := kexAlgoMap[t.algorithms.kex]
707+
kex, ok := kexAlgoMap[t.algorithms.KeyExchange]
704708
if !ok {
705-
return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex)
709+
return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.KeyExchange)
706710
}
707711

708712
var result *kexResult
@@ -809,12 +813,12 @@ func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner {
809813
}
810814

811815
func (t *handshakeTransport) server(kex kexAlgorithm, magics *handshakeMagics) (*kexResult, error) {
812-
hostKey := pickHostKey(t.hostKeys, t.algorithms.hostKey)
816+
hostKey := pickHostKey(t.hostKeys, t.algorithms.HostKey)
813817
if hostKey == nil {
814818
return nil, errors.New("ssh: internal error: negotiated unsupported signature type")
815819
}
816820

817-
r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey, t.algorithms.hostKey)
821+
r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey, t.algorithms.HostKey)
818822
return r, err
819823
}
820824

@@ -829,7 +833,7 @@ func (t *handshakeTransport) client(kex kexAlgorithm, magics *handshakeMagics) (
829833
return nil, err
830834
}
831835

832-
if err := verifyHostKeySignature(hostKey, t.algorithms.hostKey, result); err != nil {
836+
if err := verifyHostKeySignature(hostKey, t.algorithms.HostKey, result); err != nil {
833837
return nil, err
834838
}
835839

0 commit comments

Comments
 (0)