Skip to content

Commit

Permalink
Merge pull request #13 from thaJeztah/algorithms
Browse files Browse the repository at this point in the history
add notes and godoc about registering algorithms
  • Loading branch information
milosgajdos authored Jul 18, 2024
2 parents ff14faf + 284a39e commit 371b75e
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 11 deletions.
38 changes: 34 additions & 4 deletions normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,20 @@ type normalizedNamed interface {
Familiar() Named
}

// ParseNormalizedNamed parses a string into a named reference
// transforming a familiar name from Docker UI to a fully
// qualified reference. If the value may be an identifier
// use ParseAnyReference.
// ParseNormalizedNamed parses a string into a [Named] reference transforming a
// familiar name from Docker UI to a fully qualified reference. If the value may
// be an identifier, use [ParseAnyReference] instead. It returns a nil reference
// if an error occurs.
//
// An error is returned when parsing a reference that contains a digest with an
// algorithm that is not registered. Implementations must register the algorithm
// by importing the appropriate implementation.
//
// For example, to register the sha256 algorithm implementation from Go's stdlib:
//
// import (
// _ "crypto/sha256"
// )
func ParseNormalizedNamed(s string) (Named, error) {
if ok := anchoredIdentifierRegexp.MatchString(s); ok {
return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
Expand Down Expand Up @@ -104,6 +114,16 @@ type namedTaggedDigested interface {
//
// // Already a named reference
// docker.io/library/busybox:latest
//
// An error is returned when parsing a reference that contains a digest with an
// algorithm that is not registered. Implementations must register the algorithm
// by importing the appropriate implementation.
//
// For example, to register the sha256 algorithm implementation from Go's stdlib:
//
// import (
// _ "crypto/sha256"
// )
func ParseDockerRef(ref string) (Named, error) {
named, err := ParseNormalizedNamed(ref)
if err != nil {
Expand Down Expand Up @@ -243,6 +263,16 @@ func TagNameOnly(ref Named) Named {

// ParseAnyReference parses a reference string as a possible identifier,
// full digest, or familiar name.
//
// An error is returned when parsing a reference that contains a digest with an
// algorithm that is not registered. Implementations must register the algorithm
// by importing the appropriate implementation.
//
// For example, to register the sha256 algorithm implementation from Go's stdlib:
//
// import (
// _ "crypto/sha256"
// )
func ParseAnyReference(ref string) (Reference, error) {
if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
return digestReference("sha256:" + ref), nil
Expand Down
50 changes: 47 additions & 3 deletions reference.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Package reference provides a general type to represent any way of referencing images within the registry.
// Its main purpose is to abstract tags and digests (content-addressable hash).
//
// Grammar
// # Grammar
//
// reference := name [ ":" tag ] [ "@" digest ]
// name := [domain '/'] remote-name
Expand All @@ -24,6 +24,29 @@
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
//
// identifier := /[a-f0-9]{64}/
//
// # Supported algorithms
//
// Implementations must register the algorithms they want to support for digests
// by importing the appropriate implementation in a non-test file.
//
// For example, to register the sha256 algorithm implementation from Go's stdlib:
//
// import (
// _ "crypto/sha256"
// )
//
// Even though digests may be assemblable as a string, always verify your input
// with [digest.Parse] or use [digest.Digest.Validate] when accepting untrusted
// input. While there are measures to avoid common problems, this ensures you
// have valid digests in the rest of your application.
//
// While alternative encodings of hash values (digests) are possible (for example,
// base64), this package deals exclusively with hex-encoded digests. Refer to
// the [OCI image specification] for algorithms that are defined as part of the
// specification.
//
// [OCI image specification]: https://github.com/opencontainers/image-spec/blob/v1.0.2/descriptor.md#registered-algorithms
package reference

import (
Expand Down Expand Up @@ -102,6 +125,16 @@ func (f Field) MarshalText() (p []byte, err error) {
// UnmarshalText parses text bytes by invoking the
// reference parser to ensure the appropriately
// typed reference object is wrapped by field.
//
// An error is returned when unmarshaling a reference that contains a digest with an
// algorithm that is not registered. Implementations must register the algorithm
// by importing the appropriate implementation.
//
// For example, to register the sha256 algorithm implementation from Go's stdlib:
//
// import (
// _ "crypto/sha256"
// )
func (f *Field) UnmarshalText(p []byte) error {
r, err := Parse(string(p))
if err != nil {
Expand Down Expand Up @@ -181,8 +214,19 @@ func splitDomain(name string) (string, string) {
return match[1], match[2]
}

// Parse parses s and returns a syntactically valid Reference.
// If an error was encountered it is returned, along with a nil Reference.
// Parse parses s and returns a syntactically valid [Reference]. It returns a
// nil Reference if an error occurs. When parsing a reference that contains a
// digest, an error is returned if the digest's algorithm that is not registered.
//
// An error is returned when parsing a reference that contains a digest with an
// algorithm that is not registered. Implementations must register the algorithm
// by importing the appropriate implementation.
//
// For example, to register the sha256 algorithm implementation from Go's stdlib:
//
// import (
// _ "crypto/sha256"
// )
func Parse(s string) (Reference, error) {
matches := ReferenceRegexp.FindStringSubmatch(s)
if matches == nil {
Expand Down
12 changes: 8 additions & 4 deletions reference_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package reference

import (
_ "crypto/sha256"
_ "crypto/sha512"
_ "crypto/sha256" // make sure the sha256 algorithm is registered, as it's used in tests.
"encoding/json"
"errors"
"strings"
Expand Down Expand Up @@ -98,6 +97,11 @@ func TestReferenceParse(t *testing.T) {
input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
err: digest.ErrDigestUnsupported,
},
{
// sha512 is valid, but not registered by default ("crypto/sha512" is not imported)
input: "validname@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
err: digest.ErrDigestUnsupported,
},
{
input: "Uppercase:tag",
err: ErrNameContainsUppercase,
Expand Down Expand Up @@ -155,11 +159,11 @@ func TestReferenceParse(t *testing.T) {
tag: "xn--n3h.com",
},
{
input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode
input: "xn--7o8h.com/myimage:xn--7o8h.com@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode
domain: "xn--7o8h.com",
repository: "xn--7o8h.com/myimage",
tag: "xn--7o8h.com",
digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
},
{
input: "foo_bar.com:8080",
Expand Down

0 comments on commit 371b75e

Please sign in to comment.