Skip to content

Commit

Permalink
Downgrade SSH servers to use legacy algorithm lists
Browse files Browse the repository at this point in the history
- For full backwards compatibility with existing clients
  • Loading branch information
rod-hynes committed Jan 22, 2024
1 parent 4d1779d commit fad1c14
Showing 1 changed file with 68 additions and 15 deletions.
83 changes: 68 additions & 15 deletions psiphon/common/crypto/ssh/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit fad1c14

Please sign in to comment.