Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

algorithm: ErrDigestInvalidFormat on Validate() even for unknown algorithms #36

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ var (
SHA384: regexp.MustCompile(`^[a-f0-9]{96}$`),
SHA512: regexp.MustCompile(`^[a-f0-9]{128}$`),
}

algorithmRegexp = regexp.MustCompile(`[a-z0-9]+(?:[.+_-][a-z0-9]+)*`)
algorithmRegexpAnchored = regexp.MustCompile(`^` + algorithmRegexp.String() + `$`)
encodedRegexp = regexp.MustCompile(`[a-zA-Z0-9=_-]+`)
encodedRegexpAnchored = regexp.MustCompile(`^` + encodedRegexp.String() + `$`)
)

// Available returns true if the digest type is available for use. If this
Expand Down Expand Up @@ -178,15 +183,28 @@ func (a Algorithm) FromString(s string) Digest {
func (a Algorithm) Validate(encoded string) error {
r, ok := anchoredEncodedRegexps[a]
if !ok {
return ErrDigestUnsupported
r = encodedRegexpAnchored
}
// Digests much always be hex-encoded, ensuring that their hex portion will
// always be size*2
if a.Size()*2 != len(encoded) {
return ErrDigestInvalidLength
if a.Available() {
// Digests much always be hex-encoded, ensuring that their hex portion will
// always be size*2
if a.Size()*2 != len(encoded) {
return ErrDigestInvalidLength
}
}
if r.MatchString(encoded) {
return nil
if ok {
return nil
}
return ErrDigestUnsupported
}
return ErrDigestInvalidFormat
}

// ValidateIdentifier validates the algorithm name.
func (a Algorithm) ValidateIdentifier() error {
if !algorithmRegexpAnchored.MatchString(a.String()) {
return ErrDigestInvalidFormat
}
return nil
}
104 changes: 104 additions & 0 deletions algorithm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,107 @@ func TestFroms(t *testing.T) {
}
}
}

func TestValidate(t *testing.T) {
for _, testcase := range []struct {
encoded string
algorithm Algorithm
err error
}{
{
encoded: "e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b",
algorithm: "sha256",
},
{
encoded: "d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d",
algorithm: "sha384",
},
{
// empty encoded
encoded: "",
algorithm: "sha256",
err: ErrDigestInvalidLength,
},
{
// not hex (contains an m)
encoded: "d41d8cd98f00b204e9800m98ecf8427e",
algorithm: "sha256",
err: ErrDigestInvalidLength,
},
{
// too short (sha256)
encoded: "abcdef0123456789",
algorithm: "sha256",
err: ErrDigestInvalidLength,
},
{
// too short (sha512)
encoded: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
algorithm: "sha512",
err: ErrDigestInvalidLength,
},
{
encoded: "d41d8cd98f00b204e9800998ecf8427e",
algorithm: "foo",
err: ErrDigestUnsupported,
},
{
// unsupported, but the encoded part cannot possibly be valid because it contains invalid characters
encoded: "!",
algorithm: "foo",
err: ErrDigestInvalidFormat,
},
{
// uppercase
encoded: "E58FCF7418D4390DEC8E8FB69D88C06EC07039D651FEDD3AA72AF9972E7D046B",
algorithm: "sha256",
err: ErrDigestInvalidFormat,
},
} {
err := testcase.algorithm.Validate(testcase.encoded)
if err != testcase.err {
t.Fatalf("error differed from expected while validating %q: %v != %v", testcase.encoded, err, testcase.err)
}
}
}

func TestValidateIdentifier(t *testing.T) {
for _, testcase := range []struct {
algorithm Algorithm
err error
}{
{
algorithm: "sha256",
},
{
algorithm: "sha384",
},
{
// empty identifier
algorithm: "",
err: ErrDigestInvalidFormat,
},
{
// repeated separators
algorithm: "sha384__foo+bar",
err: ErrDigestInvalidFormat,
},
{
// ensure that we parse valid separators
algorithm: "sha384.foo+bar",
},
{
// ensure that we parse valid separators
algorithm: "sha384_foo+bar",
},
{
// ensure that we parse valid separators
algorithm: "sha256+b64",
},
} {
err := testcase.algorithm.ValidateIdentifier()
if err != testcase.err {
t.Fatalf("error differed from expected while validating identifier %q: %v != %v", testcase.algorithm, err, testcase.err)
}
}
}
21 changes: 14 additions & 7 deletions digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func NewDigestFromEncoded(alg Algorithm, encoded string) Digest {
}

// DigestRegexp matches valid digest types.
var DigestRegexp = regexp.MustCompile(`[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+`)
var DigestRegexp = regexp.MustCompile(algorithmRegexp.String() + `:` + encodedRegexp.String())

// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match.
var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`)
Expand Down Expand Up @@ -99,20 +99,27 @@ func FromString(s string) Digest {

// Validate checks that the contents of d is a valid digest, returning an
// error if not.
func (d Digest) Validate() error {
func (d Digest) Validate() (err error) {
s := string(d)
i := strings.Index(s, ":")
if i <= 0 || i+1 == len(s) {
return ErrDigestInvalidFormat
}
algorithm, encoded := Algorithm(s[:i]), s[i+1:]
if !algorithm.Available() {
if !DigestRegexpAnchored.MatchString(s) {
return ErrDigestInvalidFormat

err = algorithm.Validate(encoded)
if err == ErrDigestUnsupported {
err2 := algorithm.ValidateIdentifier()
if err2 != nil {
return err2
}
return ErrDigestUnsupported
return err
}
if err != nil {
return err
}
return algorithm.Validate(encoded)

return algorithm.ValidateIdentifier()
}

// Algorithm returns the algorithm portion of the digest. This will panic if
Expand Down
5 changes: 5 additions & 0 deletions digest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ func TestParseDigest(t *testing.T) {
input: "foo:d41d8cd98f00b204e9800998ecf8427e",
err: ErrDigestUnsupported,
},
{
// unsupported, but the encoded part cannot possibly be valid because it contains invalid characters
input: "foo:!",
err: ErrDigestInvalidFormat,
},
{
// repeated separators
input: "sha384__foo+bar:d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d",
Expand Down