Преглед изворни кода

Downgrade SSH servers to use legacy algorithm lists

- For full backwards compatibility with existing clients
Rod Hynes пре 2 година
родитељ
комит
fad1c14afd
1 измењених фајлова са 68 додато и 15 уклоњено
  1. 68 15
      psiphon/common/crypto/ssh/handshake.go

+ 68 - 15
psiphon/common/crypto/ssh/handshake.go

@@ -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{
+			"aes128-gcm@openssh.com",
+			chacha20Poly1305ID,
+			"aes128-ctr", "aes192-ctr", "aes256-ctr",
+		}
+		legacyServerMACs := []string{
+			"hmac-sha2-256-etm@openssh.com",
+			"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...),
 				"kex-strict-s-v00@openssh.com")
+			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
 			}