Skip to content

Commit

Permalink
disallow upper characters (/A-F/) in hex-encoded portion
Browse files Browse the repository at this point in the history
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
  • Loading branch information
AkihiroSuda committed May 19, 2017
1 parent eaa6054 commit 8a797a1
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 17 deletions.
32 changes: 29 additions & 3 deletions algorithm.go
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"
"hash"
"io"
"regexp"
)

// Algorithm identifies and implementation of a digester by an identifier.
Expand All @@ -28,9 +29,9 @@ type Algorithm string

// supported digest types
const (
SHA256 Algorithm = "sha256" // sha256 with hex encoding
SHA384 Algorithm = "sha384" // sha384 with hex encoding
SHA512 Algorithm = "sha512" // sha512 with hex encoding
SHA256 Algorithm = "sha256" // sha256 with hex encoding (without upper hex characters. i.e. /A-F/)
SHA384 Algorithm = "sha384" // sha384 with hex encoding (ditto)
SHA512 Algorithm = "sha512" // sha512 with hex encoding (ditto)

// Canonical is the primary digest algorithm used with the distribution
// project. Other digests may be used but this one is the primary storage
Expand All @@ -50,6 +51,14 @@ var (
SHA384: crypto.SHA384,
SHA512: crypto.SHA512,
}

// anchoredEncodedRegexps contains anchored regular expressions for hex-encoded digests.
// Note that /A-F/ disallowed.
anchoredEncodedRegexps = map[Algorithm]*regexp.Regexp{
SHA256: regexp.MustCompile(`^[a-f0-9]{64}$`),
SHA384: regexp.MustCompile(`^[a-f0-9]{96}$`),
SHA512: regexp.MustCompile(`^[a-f0-9]{128}$`),
}
)

// Available returns true if the digest type is available for use. If this
Expand Down Expand Up @@ -164,3 +173,20 @@ func (a Algorithm) FromBytes(p []byte) Digest {
func (a Algorithm) FromString(s string) Digest {
return a.FromBytes([]byte(s))
}

// Validate validates the encoded portion string
func (a Algorithm) Validate(encoded string) error {
r, ok := anchoredEncodedRegexps[a]
if !ok {
return ErrDigestUnsupported
}
// 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
}
return ErrDigestInvalidFormat
}
23 changes: 9 additions & 14 deletions digest.go
Expand Up @@ -101,26 +101,21 @@ func FromString(s string) Digest {
// error if not.
func (d Digest) Validate() error {
s := string(d)

i := strings.Index(s, ":")

// validate i then run through regexp
if i < 0 || i+1 == len(s) || !DigestRegexpAnchored.MatchString(s) {
if i < 0 || i+1 == len(s) {
return ErrDigestInvalidFormat
}
algorithm, encoded := Algorithm(s[:i]), s[i+1:]
if algorithm == "" {
return ErrDigestInvalidFormat
}

algorithm := Algorithm(s[:i])
if !algorithm.Available() {
if !DigestRegexpAnchored.MatchString(s) {
return ErrDigestInvalidFormat
}
return ErrDigestUnsupported
}

// Digests much always be hex-encoded, ensuring that their hex portion will
// always be size*2
if algorithm.Size()*2 != len(s[i+1:]) {
return ErrDigestInvalidLength
}

return nil
return algorithm.Validate(encoded)
}

// Algorithm returns the algorithm portion of the digest. This will panic if
Expand Down
4 changes: 4 additions & 0 deletions digest_test.go
Expand Up @@ -93,6 +93,10 @@ func TestParseDigest(t *testing.T) {
encoded: "LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564",
err: ErrDigestUnsupported,
},
{
input: "sha256:E58FCF7418D4390DEC8E8FB69D88C06EC07039D651FEDD3AA72AF9972E7D046B",
err: ErrDigestInvalidFormat,
},
} {
digest, err := Parse(testcase.input)
if err != testcase.err {
Expand Down

0 comments on commit 8a797a1

Please sign in to comment.