From 1eb9a6c5200bec2110b6efdd0191d00438664549 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Thu, 24 Sep 2026 21:08:59 +1000 Subject: [PATCH] fix(shacrypt): return VariantNone for unknown identifiers NewVariant returned VariantSHA512 for any identifier it did not recognise, so the VariantNone checks relying on it could never fail. As a result Decode accepted digests from other algorithms, such as md5crypt ($1$) or bcrypt ($2b$), and treated them as SHA512 digests, and WithVariantName silently selected SHA512 for invalid names instead of returning an error. NewVariant now returns VariantNone for unknown identifiers, so Decode rejects them with ErrEncodedHashInvalidIdentifier and WithVariantName returns ErrParameterInvalid, consistent with the other algorithms. Decoding through crypt.Decoder is unaffected as only the 5 and 6 identifiers are registered. --- algorithm/shacrypt/shacrypt_test.go | 8 +++++++- algorithm/shacrypt/variant.go | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/algorithm/shacrypt/shacrypt_test.go b/algorithm/shacrypt/shacrypt_test.go index 5d24bcf..e5d605d 100644 --- a/algorithm/shacrypt/shacrypt_test.go +++ b/algorithm/shacrypt/shacrypt_test.go @@ -17,7 +17,9 @@ func TestNewVariant(t *testing.T) { {"ShouldReturnSHA256ForName", "sha256", VariantSHA256}, {"ShouldReturnSHA512ForIdentifier", "6", VariantSHA512}, {"ShouldReturnSHA512ForName", "sha512", VariantSHA512}, - {"ShouldDefaultToSHA512ForUnknown", "unknown", VariantSHA512}, + {"ShouldReturnNoneForUnknown", "unknown", VariantNone}, + {"ShouldReturnNoneForEmpty", "", VariantNone}, + {"ShouldReturnNoneForMD5Crypt", "1", VariantNone}, } for _, tc := range testCases { @@ -132,6 +134,7 @@ func TestWithVariantName(t *testing.T) { {"ShouldNotErrSHA256", "sha256", ""}, {"ShouldNotErrSHA512", "sha512", ""}, {"ShouldNotErrEmpty", "", ""}, + {"ShouldErrUnknown", "unknown", "shacrypt validation error: parameter is invalid: variant identifier 'unknown' is invalid"}, } for _, tc := range testCases { @@ -371,6 +374,9 @@ func TestDecode(t *testing.T) { }{ {"ShouldFailInvalidFormat", "$", "shacrypt decode error: provided encoded hash has an invalid format"}, {"ShouldFailTooFewParts", "$5$", "shacrypt decode error: provided encoded hash has an invalid format"}, + {"ShouldFailMD5CryptDigest", "$1$saltsalt$abcdefghijklmnopqrstuv", "shacrypt decode error: provided encoded hash has an invalid identifier: identifier '1' is not an encoded shacrypt digest"}, + {"ShouldFailBCryptDigest", "$2b$12$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012", "shacrypt decode error: provided encoded hash has an invalid identifier: identifier '2b' is not an encoded shacrypt digest"}, + {"ShouldFailUnknownIdentifierWithRounds", "$7$rounds=5000$saltsalt$abcdefghijklmnopqrstuv", "shacrypt decode error: provided encoded hash has an invalid identifier: identifier '7' is not an encoded shacrypt digest"}, } for _, tc := range testCases { diff --git a/algorithm/shacrypt/variant.go b/algorithm/shacrypt/variant.go index 681ddc4..6e29751 100644 --- a/algorithm/shacrypt/variant.go +++ b/algorithm/shacrypt/variant.go @@ -15,7 +15,7 @@ func NewVariant(identifier string) Variant { case AlgIdentifierSHA512, algorithm.DigestSHA512: return VariantSHA512 default: - return VariantSHA512 + return VariantNone } }