diff --git a/algorithm.go b/algorithm.go index bdff42d..086a523 100644 --- a/algorithm.go +++ b/algorithm.go @@ -125,6 +125,14 @@ func (a Algorithm) Hash() hash.Hash { return algorithms[a].New() } +// Encode encodes the raw bytes of a digest, typically from a hash.Hash, into +// the encoded portion of the digest. +func (a Algorithm) Encode(d []byte) string { + // TODO(stevvooe): Currently, all algorithms use a hex encoding. When we + // add support for back registration, we can modify this accordingly. + return fmt.Sprintf("%x", d) +} + // FromReader returns the digest of the reader using the algorithm. func (a Algorithm) FromReader(rd io.Reader) (Digest, error) { digester := a.Digester() diff --git a/digest.go b/digest.go index 69e1d2b..71279be 100644 --- a/digest.go +++ b/digest.go @@ -45,16 +45,21 @@ func NewDigest(alg Algorithm, h hash.Hash) Digest { // functions. This is also useful for rebuilding digests from binary // serializations. func NewDigestFromBytes(alg Algorithm, p []byte) Digest { - return Digest(fmt.Sprintf("%s:%x", alg, p)) + return NewDigestFromEncoded(alg, alg.Encode(p)) } -// NewDigestFromHex returns a Digest from alg and a the hex encoded digest. +// NewDigestFromHex is deprecated. Please use NewDigestFromEncoded. func NewDigestFromHex(alg, hex string) Digest { - return Digest(fmt.Sprintf("%s:%s", alg, hex)) + return NewDigestFromEncoded(Algorithm(alg), hex) +} + +// NewDigestFromEncoded returns a Digest from alg and the encoded digest. +func NewDigestFromEncoded(alg Algorithm, encoded string) Digest { + return Digest(fmt.Sprintf("%s:%s", alg, encoded)) } // DigestRegexp matches valid digest types. -var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`) +var DigestRegexp = regexp.MustCompile(`[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+`) // DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match. var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`) @@ -133,12 +138,17 @@ func (d Digest) Verifier() Verifier { } } -// Hex returns the hex digest portion of the digest. This will panic if the +// Encoded returns the encoded portion of the digest. This will panic if the // underlying digest is not in a valid format. -func (d Digest) Hex() string { +func (d Digest) Encoded() string { return string(d[d.sepIndex()+1:]) } +// Hex is deprecated. Please use Digest.Encoded. +func (d Digest) Hex() string { + return d.Encoded() +} + func (d Digest) String() string { return string(d) } diff --git a/digest_test.go b/digest_test.go index 182f2dd..a3d60c2 100644 --- a/digest_test.go +++ b/digest_test.go @@ -23,17 +23,17 @@ func TestParseDigest(t *testing.T) { input string err error algorithm Algorithm - hex string + encoded string }{ { input: "sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b", algorithm: "sha256", - hex: "e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b", + encoded: "e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b", }, { input: "sha384:d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d", algorithm: "sha384", - hex: "d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d", + encoded: "d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d", }, { // empty hex @@ -53,7 +53,7 @@ func TestParseDigest(t *testing.T) { { // not hex input: "sha256:d41d8cd98f00b204e9800m98ecf8427e", - err: ErrDigestInvalidFormat, + err: ErrDigestInvalidLength, }, { // too short @@ -69,6 +69,30 @@ func TestParseDigest(t *testing.T) { input: "foo:d41d8cd98f00b204e9800998ecf8427e", err: ErrDigestUnsupported, }, + { + // repeated separators + input: "sha384__foo+bar:d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d", + err: ErrDigestInvalidFormat, + }, + { + // ensure that we parse, but we don't have support for the algorithm + input: "sha384.foo+bar:d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d", + algorithm: "sha384.foo+bar", + encoded: "d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d", + err: ErrDigestUnsupported, + }, + { + input: "sha384_foo+bar:d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d", + algorithm: "sha384_foo+bar", + encoded: "d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d", + err: ErrDigestUnsupported, + }, + { + input: "sha256+b64:LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564", + algorithm: "sha256+b64", + encoded: "LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564", + err: ErrDigestUnsupported, + }, } { digest, err := Parse(testcase.input) if err != testcase.err { @@ -83,8 +107,8 @@ func TestParseDigest(t *testing.T) { t.Fatalf("incorrect algorithm for parsed digest: %q != %q", digest.Algorithm(), testcase.algorithm) } - if digest.Hex() != testcase.hex { - t.Fatalf("incorrect hex for parsed digest: %q != %q", digest.Hex(), testcase.hex) + if digest.Encoded() != testcase.encoded { + t.Fatalf("incorrect hex for parsed digest: %q != %q", digest.Encoded(), testcase.encoded) } // Parse string return value and check equality @@ -98,7 +122,7 @@ func TestParseDigest(t *testing.T) { t.Fatalf("expected equal: %q != %q", newParsed, digest) } - newFromHex := NewDigestFromHex(newParsed.Algorithm().String(), newParsed.Hex()) + newFromHex := NewDigestFromEncoded(newParsed.Algorithm(), newParsed.Encoded()) if newFromHex != digest { t.Fatalf("%v != %v", newFromHex, digest) }