ssh: support aes256-cbc for passphrase-protected OpenSSH keys

The existing code for decrypting OpenSSH-format keys only allows aes256-ctr, the current ssh-keygen default.
However, the default encryption scheme was aes256-cbc until relatively recently, and some of these keys are still in use.
Support for aes256-cbc has been added.

Fixes golang/go#37939

Change-Id: I3730347109c5dd18e4cbe61b48bbca9566ad61d2
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/224817
Reviewed-by: Filippo Valsorda <filippo@golang.org>
diff --git a/ssh/keys.go b/ssh/keys.go
index 06f537c..31f2634 100644
--- a/ssh/keys.go
+++ b/ssh/keys.go
@@ -1246,15 +1246,23 @@
 		}
 		key, iv := k[:32], k[32:]
 
-		if cipherName != "aes256-ctr" {
-			return nil, fmt.Errorf("ssh: unknown cipher %q, only supports %q", cipherName, "aes256-ctr")
-		}
 		c, err := aes.NewCipher(key)
 		if err != nil {
 			return nil, err
 		}
-		ctr := cipher.NewCTR(c, iv)
-		ctr.XORKeyStream(privKeyBlock, privKeyBlock)
+		switch cipherName {
+		case "aes256-ctr":
+			ctr := cipher.NewCTR(c, iv)
+			ctr.XORKeyStream(privKeyBlock, privKeyBlock)
+		case "aes256-cbc":
+			if len(privKeyBlock)%c.BlockSize() != 0 {
+				return nil, fmt.Errorf("ssh: invalid encrypted private key length, not a multiple of the block size")
+			}
+			cbc := cipher.NewCBCDecrypter(c, iv)
+			cbc.CryptBlocks(privKeyBlock, privKeyBlock)
+		default:
+			return nil, fmt.Errorf("ssh: unknown cipher %q, only supports %q or %q", cipherName, "aes256-ctr", "aes256-cbc")
+		}
 
 		return privKeyBlock, nil
 	}
diff --git a/ssh/testdata/keys.go b/ssh/testdata/keys.go
index a7da078..f1e2fc5 100644
--- a/ssh/testdata/keys.go
+++ b/ssh/testdata/keys.go
@@ -271,6 +271,21 @@
 -----END OPENSSH PRIVATE KEY-----
 `),
 	},
+
+	3: {
+		Name:              "ed25519-encrypted-cbc",
+		EncryptionKey:     "password",
+		IncludesPublicKey: true,
+		PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABDzGKF3uX
+G1gXALZKFd6Ir4AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIDne4/teO42zTDdj
+NwxUMNpbfmp/dxgU4ZNkC3ydgcugAAAAoJ3J/oA7+iqVOz0CIUUk9ufdP1VP4jDf2um+0s
+Sgs7x6Gpyjq67Ps7wLRdSmxr/G5b+Z8dRGFYS/wUCQEe3whwuImvLyPwWjXLzkAyMzc01f
+ywBGSrHnvP82ppenc2HuTI+E05Xc02i6JVyI1ShiekQL5twoqtR6pEBZnD17UonIx7cRzZ
+gbDGyT3bXMQtagvCwoW+/oMTKXiZP5jCJpEO8=
+-----END OPENSSH PRIVATE KEY-----
+`),
+	},
 }
 
 // SKData contains a list of PubKeys backed by U2F/FIDO2 Security Keys and their test data.