diff --git a/reference/reference.go b/reference/reference.go
index 01e7fca77..d115a946d 100644
--- a/reference/reference.go
+++ b/reference/reference.go
@@ -43,6 +43,12 @@ var (
 	// ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference.
 	ErrReferenceInvalidFormat = errors.New("invalid reference format")
 
+	// ErrTagInvalidFormat represents an error while trying to parse a string as a tag.
+	ErrTagInvalidFormat = errors.New("invalid tag format")
+
+	// ErrDigestInvalidFormat represents an error while trying to parse a string as a tag.
+	ErrDigestInvalidFormat = errors.New("invalid digest format")
+
 	// ErrNameEmpty is returned for empty, invalid repository names.
 	ErrNameEmpty = errors.New("repository name must have at least one component")
 
@@ -182,6 +188,30 @@ func ParseNamed(name string) (Named, error) {
 	return repository(name), nil
 }
 
+// WithTag combines the name from "name" and the tag from "tag" to form a
+// reference incorporating both the name and the tag.
+func WithTag(name Named, tag string) (Tagged, error) {
+	if !anchoredNameRegexp.MatchString(tag) {
+		return nil, ErrTagInvalidFormat
+	}
+	return taggedReference{
+		name: name.Name(),
+		tag:  tag,
+	}, nil
+}
+
+// WithDigest combines the name from "name" and the digest from "digest" to form
+// a reference incorporating both the name and the digest.
+func WithDigest(name Named, digest digest.Digest) (Digested, error) {
+	if !anchoredDigestRegexp.MatchString(digest.String()) {
+		return nil, ErrDigestInvalidFormat
+	}
+	return canonicalReference{
+		name:   name.Name(),
+		digest: digest,
+	}, nil
+}
+
 func getBestReferenceType(ref reference) Reference {
 	if ref.name == "" {
 		// Allow digest only references
diff --git a/reference/reference_test.go b/reference/reference_test.go
index 3207f525b..d47abbf8f 100644
--- a/reference/reference_test.go
+++ b/reference/reference_test.go
@@ -395,3 +395,87 @@ func TestSerialization(t *testing.T) {
 
 	}
 }
+
+func TestWithTag(t *testing.T) {
+	testcases := []struct {
+		name     string
+		tag      string
+		combined string
+	}{
+		{
+			name:     "test.com/foo",
+			tag:      "tag",
+			combined: "test.com/foo:tag",
+		},
+		{
+			name:     "foo",
+			tag:      "tag2",
+			combined: "foo:tag2",
+		},
+		{
+			name:     "test.com:8000/foo",
+			tag:      "tag4",
+			combined: "test.com:8000/foo:tag4",
+		},
+	}
+	for _, testcase := range testcases {
+		failf := func(format string, v ...interface{}) {
+			t.Logf(strconv.Quote(testcase.name)+": "+format, v...)
+			t.Fail()
+		}
+
+		named, err := ParseNamed(testcase.name)
+		if err != nil {
+			failf("error parsing name: %s", err)
+		}
+		tagged, err := WithTag(named, testcase.tag)
+		if err != nil {
+			failf("WithTag failed: %s", err)
+		}
+		if tagged.String() != testcase.combined {
+			failf("unexpected: got %q, expected %q", tagged.String(), testcase.combined)
+		}
+	}
+}
+
+func TestWithDigest(t *testing.T) {
+	testcases := []struct {
+		name     string
+		digest   digest.Digest
+		combined string
+	}{
+		{
+			name:     "test.com/foo",
+			digest:   "sha256:1234567890098765432112345667890098765",
+			combined: "test.com/foo@sha256:1234567890098765432112345667890098765",
+		},
+		{
+			name:     "foo",
+			digest:   "sha256:1234567890098765432112345667890098765",
+			combined: "foo@sha256:1234567890098765432112345667890098765",
+		},
+		{
+			name:     "test.com:8000/foo",
+			digest:   "sha256:1234567890098765432112345667890098765",
+			combined: "test.com:8000/foo@sha256:1234567890098765432112345667890098765",
+		},
+	}
+	for _, testcase := range testcases {
+		failf := func(format string, v ...interface{}) {
+			t.Logf(strconv.Quote(testcase.name)+": "+format, v...)
+			t.Fail()
+		}
+
+		named, err := ParseNamed(testcase.name)
+		if err != nil {
+			failf("error parsing name: %s", err)
+		}
+		digested, err := WithDigest(named, testcase.digest)
+		if err != nil {
+			failf("WithDigest failed: %s", err)
+		}
+		if digested.String() != testcase.combined {
+			failf("unexpected: got %q, expected %q", digested.String(), testcase.combined)
+		}
+	}
+}
diff --git a/reference/regexp.go b/reference/regexp.go
index 579d5cda6..06ca8db3c 100644
--- a/reference/regexp.go
+++ b/reference/regexp.go
@@ -28,6 +28,13 @@ var (
 	// end of the matched string.
 	anchoredTagRegexp = regexp.MustCompile(`^` + TagRegexp.String() + `$`)
 
+	// DigestRegexp matches valid digests.
+	DigestRegexp = regexp.MustCompile(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`)
+
+	// anchoredDigestRegexp matches valid digests, anchored at the start and
+	// end of the matched string.
+	anchoredDigestRegexp = regexp.MustCompile(`^` + DigestRegexp.String() + `$`)
+
 	// NameRegexp is the format for the name component of references. The
 	// regexp has capturing groups for the hostname and name part omitting
 	// the seperating forward slash from either.
@@ -35,7 +42,7 @@ var (
 
 	// ReferenceRegexp is the full supported format of a reference. The
 	// regexp has capturing groups for name, tag, and digest components.
-	ReferenceRegexp = regexp.MustCompile(`^((?:` + hostnameRegexp.String() + `/)?` + nameRegexp.String() + `)(?:[:](` + TagRegexp.String() + `))?(?:[@]([A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}))?$`)
+	ReferenceRegexp = regexp.MustCompile(`^((?:` + hostnameRegexp.String() + `/)?` + nameRegexp.String() + `)(?:[:](` + TagRegexp.String() + `))?(?:[@](` + DigestRegexp.String() + `))?$`)
 
 	// anchoredNameRegexp is used to parse a name value, capturing hostname
 	anchoredNameRegexp = regexp.MustCompile(`^(?:(` + hostnameRegexp.String() + `)/)?(` + nameRegexp.String() + `)$`)