-
Notifications
You must be signed in to change notification settings - Fork 272
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Downgrade SSH servers to use legacy algorithm lists
- For full backwards compatibility with existing clients
- Loading branch information
Showing
1 changed file
with
68 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -593,7 +593,7 @@ func (t *handshakeTransport) sendKexInit() error { | |
// | ||
// When using obfuscated SSH, where only the initial, unencrypted | ||
// packets are obfuscated, NoEncryptThenMACHash should be set. | ||
noEncryptThenMACs := []string{"hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"} | ||
noEncryptThenMACs := []string{"hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96"} | ||
|
||
if t.config.NoEncryptThenMACHash { | ||
msg.MACsClientServer = noEncryptThenMACs | ||
|
@@ -656,13 +656,54 @@ func (t *handshakeTransport) sendKexInit() error { | |
return retain(PRNG, kexAlgos, permute(PRNG, preferredKexAlgos)[0]) | ||
} | ||
|
||
// Downgrade servers to use the algorithm lists used previously in | ||
// commits before 435a6a3f. This ensures that (a) the PeerKEXPRNGSeed | ||
// mechanism used in all existing clients correctly predicts the | ||
// server's algorithms; (b) random truncation by the server doesn't | ||
// select only new algorithms unknown to existing clients. | ||
// | ||
// TODO: add a versioning mechanism, such as a SSHv2 capability, to | ||
// allow for servers with new algorithm lists, where older clients | ||
// won't try to connect to these servers, and new clients know to use | ||
// non-legacy lists in the PeerKEXPRNGSeed mechanism. | ||
legacyServerKexAlgos := []string{ | ||
kexAlgoCurve25519SHA256LibSSH, | ||
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, | ||
kexAlgoDH14SHA256, kexAlgoDH14SHA1, | ||
} | ||
legacyServerCiphers := []string{ | ||
"[email protected]", | ||
chacha20Poly1305ID, | ||
"aes128-ctr", "aes192-ctr", "aes256-ctr", | ||
} | ||
legacyServerMACs := []string{ | ||
"[email protected]", | ||
"hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", | ||
} | ||
legacyServerNoEncryptThenMACs := []string{ | ||
"hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"} | ||
|
||
isServer := t.config.PeerKEXPRNGSeed == nil | ||
|
||
PRNG := prng.NewPRNGWithSeed(t.config.KEXPRNGSeed) | ||
|
||
msg.KexAlgos = selectKexAlgos(PRNG, msg.KexAlgos) | ||
ciphers := truncate(PRNG, permute(PRNG, msg.CiphersClientServer)) | ||
startingKexAlgos := msg.KexAlgos | ||
startingCiphers := msg.CiphersClientServer | ||
startingMACs := msg.MACsClientServer | ||
|
||
if isServer { | ||
startingKexAlgos = legacyServerKexAlgos | ||
startingCiphers = legacyServerCiphers | ||
startingMACs = legacyServerMACs | ||
} | ||
|
||
msg.KexAlgos = selectKexAlgos(PRNG, startingKexAlgos) | ||
|
||
ciphers := truncate(PRNG, permute(PRNG, startingCiphers)) | ||
msg.CiphersClientServer = ciphers | ||
msg.CiphersServerClient = ciphers | ||
MACs := truncate(PRNG, permute(PRNG, msg.MACsClientServer)) | ||
|
||
MACs := truncate(PRNG, permute(PRNG, startingMACs)) | ||
msg.MACsClientServer = MACs | ||
msg.MACsServerClient = MACs | ||
|
||
|
@@ -700,30 +741,42 @@ func (t *handshakeTransport) sendKexInit() error { | |
serverKexAlgos := append( | ||
append([]string(nil), preferredKexAlgos...), | ||
"[email protected]") | ||
serverCiphers := preferredCiphers | ||
serverMACS := supportedMACs | ||
serverNoEncryptThenMACs := noEncryptThenMACs | ||
|
||
// Switch to using the legacy algorithms that the server currently | ||
// downgrades to (see comment above). | ||
// | ||
// TODO: for servers without legacy backwards compatibility | ||
// concerns, skip the following lines. | ||
serverKexAlgos = legacyServerKexAlgos | ||
serverCiphers = legacyServerCiphers | ||
serverMACS = legacyServerMACs | ||
serverNoEncryptThenMACs = legacyServerNoEncryptThenMACs | ||
|
||
peerKexAlgos := selectKexAlgos(PeerPRNG, serverKexAlgos) | ||
serverKexAlgos = selectKexAlgos(PeerPRNG, serverKexAlgos) | ||
|
||
if _, err := findCommon("", msg.KexAlgos, peerKexAlgos); err != nil { | ||
if kexAlgo, ok := firstKexAlgo(peerKexAlgos); ok { | ||
if _, err := findCommon("", msg.KexAlgos, serverKexAlgos); err != nil { | ||
if kexAlgo, ok := firstKexAlgo(serverKexAlgos); ok { | ||
msg.KexAlgos = retain(PRNG, msg.KexAlgos, kexAlgo) | ||
} | ||
} | ||
|
||
peerCiphers := truncate(PeerPRNG, permute(PeerPRNG, preferredCiphers)) | ||
if _, err := findCommon("", ciphers, peerCiphers); err != nil { | ||
ciphers = retain(PRNG, ciphers, peerCiphers[0]) | ||
serverCiphers = truncate(PeerPRNG, permute(PeerPRNG, serverCiphers)) | ||
if _, err := findCommon("", ciphers, serverCiphers); err != nil { | ||
ciphers = retain(PRNG, ciphers, serverCiphers[0]) | ||
msg.CiphersClientServer = ciphers | ||
msg.CiphersServerClient = ciphers | ||
} | ||
|
||
peerMACs := supportedMACs | ||
if t.config.NoEncryptThenMACHash { | ||
peerMACs = noEncryptThenMACs | ||
serverMACS = serverNoEncryptThenMACs | ||
} | ||
|
||
peerMACs = truncate(PeerPRNG, permute(PeerPRNG, peerMACs)) | ||
if _, err := findCommon("", MACs, peerMACs); err != nil { | ||
MACs = retain(PRNG, MACs, peerMACs[0]) | ||
serverMACS = truncate(PeerPRNG, permute(PeerPRNG, serverMACS)) | ||
if _, err := findCommon("", MACs, serverMACS); err != nil { | ||
MACs = retain(PRNG, MACs, serverMACS[0]) | ||
msg.MACsClientServer = MACs | ||
msg.MACsServerClient = MACs | ||
} | ||
|