ssh: limit bcrypt KDF rounds in OpenSSH private key decryption
passphraseProtectedOpenSSHKey decodes the bcrypt round count from the key file and passes it directly to bcrypt_pbkdf.Key, whose running time is linear in that count. Files specifying very large round values cause the decryption to consume excessive CPU time: a uint32 maximum is several months of work on commodity hardware. Cap the accepted round count at 2048 (128x the default of 16, a few seconds of CPU on a modern core). OpenSSH itself does not impose an upper bound, but accepting arbitrary values turns key loading into a resource-exhaustion footgun for any code that processes files supplied by end users. This issue was found during a security audit by NCC Group Cryptography Services, sponsored by Teleport, and was assessed and is being fixed as a non-security bug. Change-Id: I01112bdf1b484ae4fab5dc8841d1a272f112df74 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/782422 Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Junyang Shao <shaojunyang@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
+11
@@ -1466,6 +1466,17 @@ func passphraseProtectedOpenSSHKey(passphrase []byte) openSSHDecryptFunc {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// OpenSSH does not impose an upper bound on the bcrypt round count
|
||||
// stored in the key file, but bcrypt_pbkdf cost is linear in rounds:
|
||||
// the default is 16, ssh-keygen lets users pick anything up to
|
||||
// INT_MAX. Cap at 2048 (128x the default, a few seconds of CPU) so
|
||||
// that an oversized value in the file cannot tie up the caller for
|
||||
// months.
|
||||
const maxRounds = 1 << 11
|
||||
if opts.Rounds > maxRounds {
|
||||
return nil, fmt.Errorf("ssh: bcrypt KDF rounds %d exceed maximum %d", opts.Rounds, maxRounds)
|
||||
}
|
||||
|
||||
k, err := bcrypt_pbkdf.Key(passphrase, []byte(opts.Salt), int(opts.Rounds), 32+16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user