diff --git a/algorithm/md5crypt/digest.go b/algorithm/md5crypt/digest.go index 66a6613..bb5afbe 100644 --- a/algorithm/md5crypt/digest.go +++ b/algorithm/md5crypt/digest.go @@ -89,8 +89,4 @@ func (d *Digest) defaults() { default: d.variant = variantDefault } - - if d.iterations < IterationsMin { - d.iterations = IterationsDefault - } } diff --git a/algorithm/md5crypt/hasher.go b/algorithm/md5crypt/hasher.go index a02d504..e2d9986 100644 --- a/algorithm/md5crypt/hasher.go +++ b/algorithm/md5crypt/hasher.go @@ -29,6 +29,7 @@ type Hasher struct { variant Variant iterations uint32 + i bool bytesSalt int @@ -128,6 +129,10 @@ func (h *Hasher) defaults() { h.d = true + if !h.i { + h.iterations = IterationsDefault + } + if h.bytesSalt < SaltLengthMin { h.bytesSalt = SaltLengthDefault } diff --git a/algorithm/md5crypt/opts.go b/algorithm/md5crypt/opts.go index 2ce9893..647a6af 100644 --- a/algorithm/md5crypt/opts.go +++ b/algorithm/md5crypt/opts.go @@ -47,14 +47,15 @@ func WithVariantName(identifier string) Opt { } // WithIterations sets the iterations parameter of the resulting md5crypt.Digest. Only valid for the Sun variant. This -// is encoded in the hash with the 'iterations' parameter. -// Minimum is 0, Maximum is 4294967295. Default is 34000. +// is encoded in the hash with the 'rounds' parameter. +// Minimum is 0, Maximum is 4294963199. Default is 34000. func WithIterations(iterations uint32) Opt { return func(h *Hasher) (err error) { if iterations < IterationsMin || iterations > IterationsMax { return fmt.Errorf(algorithm.ErrFmtHasherValidation, AlgName, fmt.Errorf(algorithm.ErrFmtInvalidIntParameter, algorithm.ErrParameterInvalid, "iterations", IterationsMin, "", IterationsMax, iterations)) } + h.i = true h.iterations = iterations return nil diff --git a/algorithm/md5crypt/regression_test.go b/algorithm/md5crypt/regression_test.go index a388347..6ff8de8 100644 --- a/algorithm/md5crypt/regression_test.go +++ b/algorithm/md5crypt/regression_test.go @@ -79,3 +79,48 @@ func TestWithIterationsRejectsRoundsThatOverflow(t *testing.T) { assert.NoError(t, err) } + +func TestSunVariantAppliesDefaultIterations(t *testing.T) { + hasher, err := New(WithVariant(VariantSun)) + require.NoError(t, err) + + digest, err := hasher.Hash("password") + require.NoError(t, err) + + encoded := digest.Encode() + assert.Contains(t, encoded, "$md5,rounds=34000$") + + decoded, err := Decode(encoded) + require.NoError(t, err, "encoded digest %q could not be decoded", encoded) + + assert.Equal(t, encoded, decoded.Encode()) + assert.True(t, decoded.Match("password")) + assert.False(t, decoded.Match("incorrect")) +} + +func TestSunVariantHonoursExplicitZeroIterations(t *testing.T) { + hasher, err := New(WithVariant(VariantSun), WithIterations(0)) + require.NoError(t, err) + + digest, err := hasher.Hash("password") + require.NoError(t, err) + + encoded := digest.Encode() + assert.Regexp(t, `^\$md5\$[^$]+\$\$[^$]+$`, encoded) + + decoded, err := Decode(encoded) + require.NoError(t, err) + + assert.True(t, decoded.Match("password")) +} + +func TestStandardVariantUnaffectedByDefaultIterations(t *testing.T) { + hasher, err := New() + require.NoError(t, err) + + digest, err := hasher.Hash("password") + require.NoError(t, err) + + assert.Regexp(t, `^\$1\$[^$]+\$[^$]+$`, digest.Encode()) + assert.True(t, digest.Match("password")) +}