From 5522f1716dfe9b799b7252ce4fbab342a2fe353d Mon Sep 17 00:00:00 2001 From: LewiGoddard Date: Mon, 14 Nov 2022 12:15:57 -0800 Subject: [PATCH] bcrypt: reject passwords longer than 72 bytes By design, bcrypt only uses the first 72 bytes of a password when generating a hash. Most implementations, including the reference one, simply silently ignore any trailing input when provided passwords longer than 72 bytes. This can cause confusion for users who expect the entire password to be used to generate the hash. In GenerateFromPassword, reject passwords longer than 72 bytes. CompareHashAndPassword will still accept these passwords, since we cannot break hashes that have already been stored. Fixes golang/go#36546 Change-Id: I039addd2a2961a7fa9d1e4a3e892a9e3c8bf4c9a Reviewed-on: https://go-review.googlesource.com/c/crypto/+/450415 Reviewed-by: Damien Neil Reviewed-by: Jason McNeil TryBot-Result: Gopher Robot Reviewed-by: Filippo Valsorda Auto-Submit: Roland Shoemaker Run-TryBot: Roland Shoemaker --- bcrypt/bcrypt.go | 9 +++++++++ bcrypt/bcrypt_test.go | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/bcrypt/bcrypt.go b/bcrypt/bcrypt.go index addf56b..5577c0f 100644 --- a/bcrypt/bcrypt.go +++ b/bcrypt/bcrypt.go @@ -82,11 +82,20 @@ type hashed struct { minor byte } +// ErrPasswordTooLong is returned when the password passed to +// GenerateFromPassword is too long (i.e. > 72 bytes). +var ErrPasswordTooLong = errors.New("bcrypt: password length exceeds 72 bytes") + // GenerateFromPassword returns the bcrypt hash of the password at the given // cost. If the cost given is less than MinCost, the cost will be set to // DefaultCost, instead. Use CompareHashAndPassword, as defined in this package, // to compare the returned hashed password with its cleartext version. +// GenerateFromPassword does not accept passwords longer than 72 bytes, which +// is the longest password bcrypt will operate on. func GenerateFromPassword(password []byte, cost int) ([]byte, error) { + if len(password) > 72 { + return nil, ErrPasswordTooLong + } p, err := newFromPassword(password, cost) if err != nil { return nil, err diff --git a/bcrypt/bcrypt_test.go b/bcrypt/bcrypt_test.go index b7162d8..8b589e3 100644 --- a/bcrypt/bcrypt_test.go +++ b/bcrypt/bcrypt_test.go @@ -241,3 +241,10 @@ func TestNoSideEffectsFromCompare(t *testing.T) { t.Errorf("got=%q want=%q", got, want) } } + +func TestPasswordTooLong(t *testing.T) { + _, err := GenerateFromPassword(make([]byte, 73), 1) + if err != ErrPasswordTooLong { + t.Errorf("unexpected error: got %q, want %q", err, ErrPasswordTooLong) + } +}