Repository: deislabs/ratify
Pull request #80 (2021-11-16 23:27:44)

minor fix: update the license in readme
Signed-off-by: Bin Du <bindu@microsoft.com>


Patch:
diff --git a/README.md b/README.md
index 6960fa4..c52fa75 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,7 @@ Usage:
 Available Commands:
   completion  generate the autocompletion script for the specified shell
   discover    Discover referrers for a subject
+  resolve     Resolve digest of a subject that is referenced by a tag
   help        Help about any command
   referrer    Discover referrers for a subject
   serve       Run ratify as a server
@@ -171,21 +172,18 @@ EOF
 
 #### Discover the graph
 
-> Please make sure that the subject is referenced with `digest` rather
-than with the tag.
+> If the subject is referenced by tag, it is resolved to digest before verifying it.
 
 ```bash
-export IMAGE_DIGEST_REF=$(docker image inspect $IMAGE | jq -r '.[0].RepoDigests[0]')
-
 # Discover the graph
-ratify discover -s $IMAGE_DIGEST_REF
+ratify discover -s $IMAGE
 ```
 
 #### Verify the graph
 
 ```bash
 # Verify the graph
-ratify verify -s $IMAGE_DIGEST_REF
+ratify verify -s $IMAGE
 ```
 
 ## Documents
@@ -209,7 +207,7 @@ The Ratify release process is defined in [RELEASES.md](./RELEASES.md).
 
 ## Licensing
 
-This project is released under the [MIT License](./LICENSE).
+This project is released under the [Apache-2.0 License](./LICENSE).
 
 ## Trademark
 
diff --git a/cmd/ratify/cmd/discover.go b/cmd/ratify/cmd/discover.go
index b371ebe..ef58b4b 100644
--- a/cmd/ratify/cmd/discover.go
+++ b/cmd/ratify/cmd/discover.go
@@ -27,6 +27,7 @@ import (
 	"github.com/deislabs/ratify/pkg/ocispecs"
 	"github.com/deislabs/ratify/pkg/referrerstore"
 	sf "github.com/deislabs/ratify/pkg/referrerstore/factory"
+	su "github.com/deislabs/ratify/pkg/referrerstore/utils"
 	"github.com/deislabs/ratify/pkg/utils"
 	"github.com/spf13/cobra"
 	"github.com/xlab/treeprint"
@@ -116,6 +117,14 @@ func discover(opts discoverCmdOptions) error {
 		References []ocispecs.ReferenceDescriptor
 	}
 
+	if subRef.Digest == "" {
+		desc, err := su.ResolveSubjectDescriptor(context.Background(), &stores, subRef)
+		if err != nil {
+			return err
+		}
+		subRef.Digest = desc.Digest
+	}
+
 	var results []listResult
 
 	for _, referrerStore := range stores {
diff --git a/cmd/ratify/cmd/resolve.go b/cmd/ratify/cmd/resolve.go
new file mode 100644
index 0000000..b74ff15
--- /dev/null
+++ b/cmd/ratify/cmd/resolve.go
@@ -0,0 +1,99 @@
+/*
+Copyright The Ratify Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/deislabs/ratify/config"
+	sf "github.com/deislabs/ratify/pkg/referrerstore/factory"
+	su "github.com/deislabs/ratify/pkg/referrerstore/utils"
+	"github.com/deislabs/ratify/pkg/utils"
+	"github.com/spf13/cobra"
+)
+
+const (
+	resolveUse = "resolve"
+)
+
+type resolveCmdOptions struct {
+	configFilePath string
+	subject        string
+}
+
+func NewCmdResolve(argv ...string) *cobra.Command {
+
+	if len(argv) == 0 {
+		argv = []string{os.Args[0]}
+	}
+
+	eg := fmt.Sprintf(`  # Resolve digest of a subject that is referenced by a tag
+  %s resolve -c ./config.yaml -s myregistry/myrepo:v1`, strings.Join(argv, " "))
+
+	var opts resolveCmdOptions
+
+	cmd := &cobra.Command{
+		Use:     resolveUse,
+		Short:   "Resolve digest of a subject that is referenced by a tag",
+		Example: eg,
+		Args:    cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return resolve(opts)
+		},
+	}
+
+	flags := cmd.Flags()
+
+	flags.StringVarP(&opts.subject, "subject", "s", "", "Subject Reference")
+	flags.StringVarP(&opts.configFilePath, "config", "c", "", "Config File Path")
+	return cmd
+}
+
+func resolve(opts resolveCmdOptions) error {
+
+	if opts.subject == "" {
+		return errors.New("subject parameter is required")
+	}
+
+	subRef, err := utils.ParseSubjectReference(opts.subject)
+	if err != nil {
+		return err
+	}
+
+	cf, err := config.Load(opts.configFilePath)
+	if err != nil {
+		return err
+	}
+
+	stores, err := sf.CreateStoresFromConfig(cf.StoresConfig, config.GetDefaultPluginPath())
+
+	if err != nil {
+		return err
+	}
+
+	result, err := su.ResolveSubjectDescriptor(context.Background(), &stores, subRef)
+
+	if err != nil {
+		return err
+	}
+
+	fmt.Println(result.Digest)
+	return nil
+}
diff --git a/cmd/ratify/cmd/root.go b/cmd/ratify/cmd/root.go
index b66502e..c0647ba 100644
--- a/cmd/ratify/cmd/root.go
+++ b/cmd/ratify/cmd/root.go
@@ -40,6 +40,7 @@ func New(use, short string) *cobra.Command {
 	root.AddCommand(NewCmdServe(use, serveUse))
 	root.AddCommand(NewCmdDiscover(use, discoverUse))
 	root.AddCommand(NewCmdVersion(use, versionUse))
+	root.AddCommand(NewCmdResolve(use, resolveUse))
 
 	// TODO debug logging
 	return root
diff --git a/docs/store.md b/docs/store.md
index b576674..8a4e261 100644
--- a/docs/store.md
+++ b/docs/store.md
@@ -8,6 +8,7 @@ A referrer store in the framework is a component that can store and distribute O
 - Retrieve manifest for a referrer identified by the OCI reference
 - Download the blobs of an artifact
 - Retrieves properties of the subject manifest like descriptor to support verification.
+- Retrieves the descriptor properties for a subject reference
 
 This document proposes a generic plugin-based solution for integrating different stores into the framework.
 
@@ -84,6 +85,7 @@ type ReferrerStore interface {
  // Used for small objects.
  GetBlobContent(ctx context.Context, subjectReference common.Reference, digest digest.Digest) ([]byte, error)
  GetReferenceManifest(ctx context.Context, subjectReference common.Reference, referenceDesc ocispecs.ReferenceDescriptor) (ocispecs.ReferenceManifest, error)
+ GetSubjectDescriptor(ctx context.Context, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error)
 }
 
 ```
@@ -105,6 +107,9 @@ This method is used to fetch the contents of a blob that is contained within the
 
 This method is used to fetch the contents of an [artifact manifest](https://github.com/oras-project/artifacts-spec/blob/main/artifact-manifest.md) that is identified by a reference descriptor. This reference descriptor is a referrer to the given subject.
 
+#### GetSubjectDescriptor
+This method is used to get the descriptor for a subject that includes digest, size and media type properties. 
+
 ### Section 3 : Plugin Based Store
 
 The framework MUST provide a reference implementation of the store interface using the [plugin architecture](https://hackmd.io/9htAyk-OQmauWPnNqMTVIw?both#Plugin-architecture) It will execute the configured plugins to implement the methods of the interface.
@@ -120,10 +125,10 @@ There are two types of inputs that are passed to the plugin. They are parameters
 
 Execution parameters are passed to the plugins via OS environment variables. The parameters that are passed to a store are defined below
 
-- **RATIFY_STORE_COMMAND** indicates the operation to be executed. Currently they include ```LISTREFERRERS```, ```GETBLOB```, ```GETREFMANIFEST```
+- **RATIFY_STORE_COMMAND** indicates the operation to be executed. Currently they include ```LISTREFERRERS```, ```GETBLOB```, ```GETREFMANIFEST```, ```GETSUBJECTDESCRIPTOR```
 - **RATIFY_STORE_SUBJECT** is the artifact under verification usually identified by a reference as per the OCI `{DNS/IP}/{Repository}:[name|digest]`
 - **RATIFY_STORE_VERSION** is the version of the specification used between the framework and plugin. This value is taken from the ```version``` field of the store configuration.
-- **RATIFY_STORE_ARGS**: Extra arguments passed in by the framework at invocation time. They are key-value pairs separated by semicolons; for example, "digest=sha256:sdfdsdss;nextToken=123;artifactTypes:type1,type2"
+- **RATIFY_STORE_ARGS**: Extra arguments passed in by the framework at invocation time. They are key-value pairs separated by semicolons; for example, "digest=sha256:sdfdsdss;nextToken=123;artifactTypes:type1,type2". If the command doesn't need any arguments, this can be empty
 
 ##### Operations & Parameters
 
@@ -169,6 +174,7 @@ The store specification defines 3 operations ```LISTREFERRERS```, ```GETBLOB```,
 
 **```GETREFMANIFEST```**: The content of the reference manifest is returned as byte array via ```stdout``
 
+**```GETSUBJECTDESCRIPTOR```**: The contents of the descriptor for the subject is returned as a byte array via ```stdout```
 #### Error
 
 Plugins should output a JSON object with the following properties if they encounter an error
@@ -179,7 +185,7 @@ Plugins should output a JSON object with the following properties if they encoun
 
 [TODO] Add the error codes after the implementation
 
-### Section 5: Plugin Implementation
+### Section 6: Plugin Implementation
 
 The framework MAY provide libraries that can provide skeletons for writing plugins. These libraries can scaffold the parameter and configuration parsing and transformation and can define methods that the plugin writers can override for the implementation. These libraries also should catch any exceptions retruned from the plugins and return a proper error result to the framework. A simple CLI for example ```ratify plugin store add mystore``` to create a stub for a plugin using these libraries MAY be provided by the framework.
 
diff --git a/pkg/executor/core/executor.go b/pkg/executor/core/executor.go
index 7af9fa8..6a9efad 100644
--- a/pkg/executor/core/executor.go
+++ b/pkg/executor/core/executor.go
@@ -25,6 +25,7 @@ import (
 	"github.com/deislabs/ratify/pkg/ocispecs"
 	"github.com/deislabs/ratify/pkg/policyprovider"
 	"github.com/deislabs/ratify/pkg/referrerstore"
+	su "github.com/deislabs/ratify/pkg/referrerstore/utils"
 	"github.com/deislabs/ratify/pkg/utils"
 	vr "github.com/deislabs/ratify/pkg/verifier"
 )
@@ -56,6 +57,14 @@ func (executor Executor) verifySubjectInternal(ctx context.Context, verifyParame
 		return types.VerifyResult{}, err
 	}
 
+	desc, err := su.ResolveSubjectDescriptor(ctx, &executor.ReferrerStores, subjectReference)
+
+	if err != nil {
+		return types.VerifyResult{}, fmt.Errorf("resolving descriptor for the subject failed with error %v", err)
+	}
+
+	subjectReference.Digest = desc.Digest
+
 	var verifierReports []interface{}
 	anyVerifySuccess := map[string]bool{}
 	for _, referenceType := range verifyParameters.ReferenceTypes {
@@ -73,7 +82,7 @@ func (executor Executor) verifySubjectInternal(ctx context.Context, verifyParame
 			for _, reference := range referrersResult.Referrers {
 
 				if executor.PolicyEnforcer.VerifyNeeded(ctx, subjectReference, reference) {
-					verifyResult := executor.verifyReference(ctx, subjectReference, reference, referrerStore)
+					verifyResult := executor.verifyReference(ctx, subjectReference, desc, reference, referrerStore)
 					verifierReports = append(verifierReports, verifyResult.VerifierReports...)
 
 					if !verifyResult.IsSuccess {
@@ -107,7 +116,7 @@ func (executor Executor) verifySubjectInternal(ctx context.Context, verifyParame
 	return types.VerifyResult{IsSuccess: overallVerifySuccess, VerifierReports: verifierReports}, nil
 }
 
-func (ex Executor) verifyReference(ctx context.Context, subjectRef common.Reference, referenceDesc ocispecs.ReferenceDescriptor, referrerStore referrerstore.ReferrerStore) types.VerifyResult {
+func (ex Executor) verifyReference(ctx context.Context, subjectRef common.Reference, subjectDesc *ocispecs.SubjectDescriptor, referenceDesc ocispecs.ReferenceDescriptor, referrerStore referrerstore.ReferrerStore) types.VerifyResult {
 	var verifyResults []interface{}
 	var isSuccess = true
 	for _, verifier := range ex.Verifiers {
diff --git a/pkg/ocispecs/referencedescriptor.go b/pkg/ocispecs/descriptor.go
similarity index 96%
rename from pkg/ocispecs/referencedescriptor.go
rename to pkg/ocispecs/descriptor.go
index 56c1feb..a41389e 100644
--- a/pkg/ocispecs/referencedescriptor.go
+++ b/pkg/ocispecs/descriptor.go
@@ -36,3 +36,7 @@ type ReferenceManifest struct {
 	Blobs        []oci.Descriptor `json:"blobs"`
 	Subjects     []oci.Descriptor `json:"manifests"`
 }
+
+type SubjectDescriptor struct {
+	oci.Descriptor
+}
diff --git a/pkg/referrerstore/api.go b/pkg/referrerstore/api.go
index fc7e8ed..22bf1a3 100644
--- a/pkg/referrerstore/api.go
+++ b/pkg/referrerstore/api.go
@@ -48,4 +48,7 @@ type ReferrerStore interface {
 
 	// GetConfig returns the configuration of this store
 	GetConfig() *config.StoreConfig
+
+	// GetSubjectDescriptor returns the descriptor for the given subject.
+	GetSubjectDescriptor(ctx context.Context, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error)
 }
diff --git a/pkg/referrerstore/mocks/types.go b/pkg/referrerstore/mocks/types.go
new file mode 100644
index 0000000..facff2f
--- /dev/null
+++ b/pkg/referrerstore/mocks/types.go
@@ -0,0 +1,63 @@
+/*
+Copyright The Ratify Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mocks
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/deislabs/ratify/pkg/common"
+	"github.com/deislabs/ratify/pkg/ocispecs"
+	"github.com/deislabs/ratify/pkg/referrerstore"
+	"github.com/deislabs/ratify/pkg/referrerstore/config"
+	"github.com/opencontainers/go-digest"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+type TestStore struct {
+	References []ocispecs.ReferenceDescriptor
+	ResolveMap map[string]digest.Digest
+}
+
+func (s *TestStore) Name() string {
+	return "test-store"
+}
+
+func (s *TestStore) ListReferrers(ctx context.Context, subjectReference common.Reference, artifactTypes []string, nextToken string) (referrerstore.ListReferrersResult, error) {
+	return referrerstore.ListReferrersResult{Referrers: s.References}, nil
+}
+
+func (s *TestStore) GetBlobContent(ctx context.Context, subjectReference common.Reference, digest digest.Digest) ([]byte, error) {
+	return nil, nil
+}
+
+func (s *TestStore) GetReferenceManifest(ctx context.Context, subjectReference common.Reference, referenceDesc ocispecs.ReferenceDescriptor) (ocispecs.ReferenceManifest, error) {
+	return ocispecs.ReferenceManifest{}, nil
+}
+
+func (s *TestStore) GetConfig() *config.StoreConfig {
+	return &config.StoreConfig{}
+}
+
+func (s *TestStore) GetSubjectDescriptor(ctx context.Context, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error) {
+	if s.ResolveMap != nil {
+		if result, ok := s.ResolveMap[subjectReference.Tag]; ok {
+			return &ocispecs.SubjectDescriptor{Descriptor: v1.Descriptor{Digest: result}}, nil
+		}
+	}
+
+	return nil, fmt.Errorf("cannot resolve digest for the subject reference")
+}
diff --git a/pkg/referrerstore/oras/oras.go b/pkg/referrerstore/oras/oras.go
index 45fb1fd..7ad9ee3 100644
--- a/pkg/referrerstore/oras/oras.go
+++ b/pkg/referrerstore/oras/oras.go
@@ -164,6 +164,30 @@ func (store *orasStore) GetReferenceManifest(ctx context.Context, subjectReferen
 	return ArtifactManifestToReferenceManifest(manifest), nil
 }
 
+func (store *orasStore) GetSubjectDescriptor(ctx context.Context, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error) {
+	ref, err := name.ParseReference(subjectReference.Original)
+	if err != nil {
+		return nil, err
+	}
+	dig, err := remote.Head(ref)
+	if err != nil {
+		return nil, err
+	}
+
+	dg, err := digest.Parse(dig.Digest.String())
+	if err != nil {
+		return nil, err
+	}
+
+	desc := oci.Descriptor{
+		MediaType: string(dig.MediaType),
+		Digest:    dg,
+		Size:      dig.Size,
+		URLs:      dig.URLs,
+	}
+	return &ocispecs.SubjectDescriptor{Descriptor: desc}, nil
+}
+
 func (store *orasStore) createRegistryClient(targetRef common.Reference) (*content.Registry, error) {
 	// TODO: support authentication
 	registryOpts := content.RegistryOptions{
diff --git a/pkg/referrerstore/plugin/const.go b/pkg/referrerstore/plugin/const.go
index fcbd541..af126e7 100644
--- a/pkg/referrerstore/plugin/const.go
+++ b/pkg/referrerstore/plugin/const.go
@@ -19,6 +19,7 @@ const (
 	ListReferrersCommand  = "LISTREFERRERS"
 	GetBlobContentCommand = "GETBLOB"
 	GetRefManifestCommand = "GETREFMANIFEST"
+	GetSubjectDescriptor  = "GETSUBJECTDESCRIPTOR"
 	CommandEnvKey         = "RATIFY_STORE_COMMAND"
 	SubjectEnvKey         = "RATIFY_STORE_SUBJECT"
 	VersionEnvKey         = "RATIFY_STORE_VERSION"
diff --git a/pkg/referrerstore/plugin/plugin.go b/pkg/referrerstore/plugin/plugin.go
index b9f05ba..a3e4fdf 100644
--- a/pkg/referrerstore/plugin/plugin.go
+++ b/pkg/referrerstore/plugin/plugin.go
@@ -162,6 +162,38 @@ func (sp *StorePlugin) GetReferenceManifest(ctx context.Context, subjectReferenc
 	return manifest, nil
 }
 
+func (sp *StorePlugin) GetSubjectDescriptor(ctx context.Context, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error) {
+	pluginPath, err := sp.executor.FindInPaths(sp.name, sp.path)
+	if err != nil {
+		return nil, err
+	}
+
+	pluginArgs := ReferrerStorePluginArgs{
+		Command:          GetSubjectDescriptor,
+		Version:          sp.version,
+		SubjectReference: subjectReference.String(),
+		PluginArgs:       [][2]string{},
+	}
+
+	storeConfigBytes, err := json.Marshal(sp.rawConfig)
+	if err != nil {
+		return nil, err
+	}
+
+	// TODO std writer
+	stdoutBytes, err := sp.executor.ExecutePlugin(ctx, pluginPath, nil, storeConfigBytes, pluginArgs.AsEnviron())
+	if err != nil {
+		return nil, err
+	}
+
+	desc, err := types.GetSubjectDescriptorResult(stdoutBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	return desc, nil
+}
+
 func (sp *StorePlugin) GetConfig() *config.StoreConfig {
 	return &config.StoreConfig{
 		Version:       sp.version,
diff --git a/pkg/referrerstore/plugin/skel/skel.go b/pkg/referrerstore/plugin/skel/skel.go
index e0ecdf2..4d318a1 100644
--- a/pkg/referrerstore/plugin/skel/skel.go
+++ b/pkg/referrerstore/plugin/skel/skel.go
@@ -44,6 +44,7 @@ type pcontext struct {
 type ListReferrers func(args *CmdArgs, subjectReference common.Reference, artifactTypes []string, nextToken string) (*referrerstore.ListReferrersResult, error)
 type GetBlobContent func(args *CmdArgs, subjectReference common.Reference, digest digest.Digest) ([]byte, error)
 type GetReferenceManifest func(args *CmdArgs, subjectReference common.Reference, digest digest.Digest) (ocispecs.ReferenceManifest, error)
+type GetSubjectDescriptor func(args *CmdArgs, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error)
 
 // CmdArgs describes different arguments that are passed when store plugin is invoked.
 type CmdArgs struct {
@@ -55,13 +56,13 @@ type CmdArgs struct {
 }
 
 // PluginMain is the core "main" for a plugin which includes error handling.
-func PluginMain(name, version string, listReferrers ListReferrers, getBlobContent GetBlobContent, getRefManifest GetReferenceManifest, supportedVersions []string) {
+func PluginMain(name, version string, listReferrers ListReferrers, getBlobContent GetBlobContent, getRefManifest GetReferenceManifest, getSubDesc GetSubjectDescriptor, supportedVersions []string) {
 	if e := (&pcontext{
 		GetEnviron: os.Getenv,
 		Stdin:      os.Stdin,
 		Stdout:     os.Stdout,
 		Stderr:     os.Stderr,
-	}).pluginMainCore(name, version, listReferrers, getBlobContent, getRefManifest, supportedVersions); e != nil {
+	}).pluginMainCore(name, version, listReferrers, getBlobContent, getRefManifest, getSubDesc, supportedVersions); e != nil {
 		if err := e.Print(); err != nil {
 			log.Print("Error writing error result to stdout: ", err)
 		}
@@ -69,7 +70,7 @@ func PluginMain(name, version string, listReferrers ListReferrers, getBlobConten
 	}
 }
 
-func (c *pcontext) pluginMainCore(name, version string, listReferrers ListReferrers, getBlobContent GetBlobContent, getRefManifest GetReferenceManifest, supportedVersions []string) *plugin.Error {
+func (c *pcontext) pluginMainCore(name, version string, listReferrers ListReferrers, getBlobContent GetBlobContent, getRefManifest GetReferenceManifest, getSubDesc GetSubjectDescriptor, supportedVersions []string) *plugin.Error {
 	cmd, cmdArgs, err := c.getCmdArgsFromEnv()
 	if err != nil {
 		return err
@@ -90,6 +91,8 @@ func (c *pcontext) pluginMainCore(name, version string, listReferrers ListReferr
 		return c.cmdGetBlob(cmdArgs, getBlobContent)
 	case sp.GetRefManifestCommand:
 		return c.cmdGetRefManifest(cmdArgs, getRefManifest)
+	case sp.GetSubjectDescriptor:
+		return c.cmdGetSubjectDescriptor(cmdArgs, getSubDesc)
 	default:
 		return plugin.NewError(types.ErrUnknownCommand, fmt.Sprintf("unknown %s: %v", sp.CommandEnvKey, cmd), "")
 	}
@@ -201,6 +204,25 @@ func (c *pcontext) cmdGetRefManifest(cmdArgs *CmdArgs, pluginFunc GetReferenceMa
 	return nil
 }
 
+func (c *pcontext) cmdGetSubjectDescriptor(cmdArgs *CmdArgs, pluginFunc GetSubjectDescriptor) *plugin.Error {
+	result, err := pluginFunc(cmdArgs, cmdArgs.subjectRef)
+
+	if err != nil {
+		return plugin.NewError(types.ErrPluginCmdFailure, fmt.Sprintf("plugin command %s failed", sp.ListReferrersCommand), err.Error())
+	}
+
+	if err != nil {
+		return plugin.NewError(types.ErrPluginCmdFailure, fmt.Sprintf("plugin command %s failed", sp.ListReferrersCommand), err.Error())
+	}
+
+	err = types.WriteSubjectDescriptorResult(result, c.Stdout)
+	if err != nil {
+		return plugin.NewError(types.ErrIOFailure, "failed to write plugin output", err.Error())
+	}
+
+	return nil
+}
+
 func (c *pcontext) getCmdArgsFromEnv() (string, *CmdArgs, *plugin.Error) {
 	argsMissing := make([]string, 0)
 
@@ -224,7 +246,7 @@ func (c *pcontext) getCmdArgsFromEnv() (string, *CmdArgs, *plugin.Error) {
 
 	// #4 Args
 	var args = c.GetEnviron(sp.ArgsEnvKey)
-	if args == "" {
+	if args == "" && cmd != sp.GetSubjectDescriptor {
 		argsMissing = append(argsMissing, sp.ArgsEnvKey)
 	}
 
diff --git a/pkg/referrerstore/types/types.go b/pkg/referrerstore/types/types.go
index a98bd04..4296bd6 100644
--- a/pkg/referrerstore/types/types.go
+++ b/pkg/referrerstore/types/types.go
@@ -61,7 +61,7 @@ func GetListReferrersResult(result []byte) (referrerstore.ListReferrersResult, e
 	}, nil
 }
 
-// GetListReferrersResult unmarshall the given JSON data to reference manifest
+// GetReferenceManifestResult unmarshall the given JSON data to reference manifest
 func GetReferenceManifestResult(result []byte) (ocispecs.ReferenceManifest, error) {
 	manifest := ocispecs.ReferenceManifest{}
 	if err := json.Unmarshal(result, &manifest); err != nil {
@@ -70,6 +70,15 @@ func GetReferenceManifestResult(result []byte) (ocispecs.ReferenceManifest, erro
 	return manifest, nil
 }
 
+// GetSubjectDescriptorResult unmarshall the given JSON data to the subject descriptor
+func GetSubjectDescriptorResult(result []byte) (*ocispecs.SubjectDescriptor, error) {
+	desc := ocispecs.SubjectDescriptor{}
+	if err := json.Unmarshal(result, &desc); err != nil {
+		return nil, err
+	}
+	return &desc, nil
+}
+
 // WriteListReferrersResult writes the list referrers result as JSON data to the given writer
 func WriteListReferrersResult(result *referrerstore.ListReferrersResult, w io.Writer) error {
 	return json.NewEncoder(w).Encode(ReferrersResult{
@@ -82,3 +91,8 @@ func WriteListReferrersResult(result *referrerstore.ListReferrersResult, w io.Wr
 func WriteReferenceManifestResult(result *ocispecs.ReferenceManifest, w io.Writer) error {
 	return json.NewEncoder(w).Encode(result)
 }
+
+// WriteSubjectDescriptorResult writes the subject descriptor as JSON data in to the given writer
+func WriteSubjectDescriptorResult(result *ocispecs.SubjectDescriptor, w io.Writer) error {
+	return json.NewEncoder(w).Encode(result)
+}
diff --git a/pkg/referrerstore/utils/utils.go b/pkg/referrerstore/utils/utils.go
new file mode 100644
index 0000000..1c0602b
--- /dev/null
+++ b/pkg/referrerstore/utils/utils.go
@@ -0,0 +1,38 @@
+/*
+Copyright The Ratify Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package utils
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/deislabs/ratify/pkg/common"
+	"github.com/deislabs/ratify/pkg/ocispecs"
+	"github.com/deislabs/ratify/pkg/referrerstore"
+	"github.com/sirupsen/logrus"
+)
+
+func ResolveSubjectDescriptor(ctx context.Context, stores *[]referrerstore.ReferrerStore, subRef common.Reference) (*ocispecs.SubjectDescriptor, error) {
+	for _, referrerStore := range *stores {
+		desc, err := referrerStore.GetSubjectDescriptor(ctx, subRef)
+		if err == nil {
+			return desc, nil
+		}
+		logrus.Warnf("failed to resolve the subject descriptor from store %s with error %v\n", referrerStore.Name(), err)
+	}
+
+	return nil, fmt.Errorf("could not resolve descriptor for a subject from any stores")
+}
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
index c37e7d8..77df8b6 100644
--- a/pkg/utils/utils.go
+++ b/pkg/utils/utils.go
@@ -53,8 +53,6 @@ func ParseSubjectReference(subRef string) (common.Reference, error) {
 
 	if digested, ok := parseResult.(reference.Digested); ok {
 		subjectRef.Digest = digested.Digest()
-	} else {
-		return common.Reference{}, fmt.Errorf("failed to parse subject reference digest")
 	}
 
 	if tag, ok := parseResult.(reference.Tagged); ok {
diff --git a/pkg/verifier/mocks/types.go b/pkg/verifier/mocks/types.go
index 206e611..8c2891a 100644
--- a/pkg/verifier/mocks/types.go
+++ b/pkg/verifier/mocks/types.go
@@ -18,38 +18,11 @@ package mocks
 import (
 	"context"
 
-	"github.com/deislabs/ratify/pkg/common"
 	"github.com/deislabs/ratify/pkg/executor"
 	"github.com/deislabs/ratify/pkg/executor/types"
-	"github.com/deislabs/ratify/pkg/ocispecs"
-	"github.com/deislabs/ratify/pkg/referrerstore"
-	"github.com/deislabs/ratify/pkg/referrerstore/config"
 	"github.com/deislabs/ratify/pkg/verifier"
-	"github.com/opencontainers/go-digest"
 )
 
-type TestStore struct{}
-
-func (s *TestStore) Name() string {
-	return "test-store"
-}
-
-func (s *TestStore) ListReferrers(ctx context.Context, subjectReference common.Reference, artifactTypes []string, nextToken string) (referrerstore.ListReferrersResult, error) {
-	return referrerstore.ListReferrersResult{}, nil
-}
-
-func (s *TestStore) GetBlobContent(ctx context.Context, subjectReference common.Reference, digest digest.Digest) ([]byte, error) {
-	return nil, nil
-}
-
-func (s *TestStore) GetReferenceManifest(ctx context.Context, subjectReference common.Reference, referenceDesc ocispecs.ReferenceDescriptor) (ocispecs.ReferenceManifest, error) {
-	return ocispecs.ReferenceManifest{}, nil
-}
-
-func (s *TestStore) GetConfig() *config.StoreConfig {
-	return &config.StoreConfig{}
-}
-
 type TestExecutor struct {
 	VerifySuccess bool
 }
diff --git a/plugins/referrerstore/sample/sample.go b/plugins/referrerstore/sample/sample.go
index 77b8199..ef0b85d 100644
--- a/plugins/referrerstore/sample/sample.go
+++ b/plugins/referrerstore/sample/sample.go
@@ -21,10 +21,11 @@ import (
 	"github.com/deislabs/ratify/pkg/referrerstore"
 	"github.com/deislabs/ratify/pkg/referrerstore/plugin/skel"
 	"github.com/opencontainers/go-digest"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
 func main() {
-	skel.PluginMain("sample", "1.0.0", ListReferrers, GetBlobContent, GetReferenceManifest, []string{"1.0.0"})
+	skel.PluginMain("sample", "1.0.0", ListReferrers, GetBlobContent, GetReferenceManifest, GetSubjectDescriptor, []string{"1.0.0"})
 }
 
 func ListReferrers(args *skel.CmdArgs, subjectReference common.Reference, artifactTypes []string, nextToken string) (*referrerstore.ListReferrersResult, error) {
@@ -44,6 +45,14 @@ func GetBlobContent(args *skel.CmdArgs, subjectReference common.Reference, diges
 	return []byte(digest.String()), nil
 }
 
+func GetSubjectDescriptor(args *skel.CmdArgs, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error) {
+	dig := subjectReference.Digest
+	if dig == "" {
+		dig = digest.FromString(subjectReference.Tag)
+	}
+	return &ocispecs.SubjectDescriptor{Descriptor: v1.Descriptor{Digest: dig}}, nil
+}
+
 func GetReferenceManifest(args *skel.CmdArgs, subjectReference common.Reference, digest digest.Digest) (ocispecs.ReferenceManifest, error) {
 	return ocispecs.ReferenceManifest{MediaType: "testMediaType", ArtifactType: "testArtifactType"}, nil
 }


Test patch:
diff --git a/pkg/executor/core/executor_test.go b/pkg/executor/core/executor_test.go
index cab1c85..57f2a1c 100644
--- a/pkg/executor/core/executor_test.go
+++ b/pkg/executor/core/executor_test.go
@@ -23,10 +23,12 @@ import (
 	"github.com/deislabs/ratify/pkg/ocispecs"
 	config "github.com/deislabs/ratify/pkg/policyprovider/configpolicy"
 	"github.com/deislabs/ratify/pkg/referrerstore"
+	"github.com/deislabs/ratify/pkg/referrerstore/mocks"
 	"github.com/deislabs/ratify/pkg/verifier"
+	"github.com/opencontainers/go-digest"
 )
 
-func TestVerifySubject_SubjectParseError(t *testing.T) {
+func TestVerifySubject_ResolveSubjectDescriptor_Failed(t *testing.T) {
 	executor := Executor{}
 
 	verifyParameters := e.VerifyParameters{
@@ -40,12 +42,41 @@ func TestVerifySubject_SubjectParseError(t *testing.T) {
 	}
 }
 
+func TestVerifySubject_ResolveSubjectDescriptor_Success(t *testing.T) {
+	testDigest := digest.FromString("test")
+	store := &mocks.TestStore{
+		References: []ocispecs.ReferenceDescriptor{},
+		ResolveMap: map[string]digest.Digest{
+			"v1": testDigest,
+		},
+	}
+
+	executor := Executor{
+		ReferrerStores: []referrerstore.ReferrerStore{store},
+	}
+
+	verifyParameters := e.VerifyParameters{
+		Subject: "localhost:5000/net-monitor:v1",
+	}
+
+	_, err := executor.verifySubjectInternal(context.Background(), verifyParameters)
+
+	if err != ReferrersNotFound {
+		t.Fatalf("expected ReferrersNotFound actual %v", err)
+	}
+}
+
 func TestVerifySubject_Verify_NoReferrers(t *testing.T) {
+	testDigest := digest.FromString("test")
 	configPolicy := config.PolicyEnforcer{}
 	ex := &Executor{
 		PolicyEnforcer: configPolicy,
-		ReferrerStores: []referrerstore.ReferrerStore{&TestStore{}},
-		Verifiers:      []verifier.ReferenceVerifier{&TestVerifier{}},
+		ReferrerStores: []referrerstore.ReferrerStore{&mocks.TestStore{
+			ResolveMap: map[string]digest.Digest{
+				"v1": testDigest,
+			},
+		}},
+		Verifiers: []verifier.ReferenceVerifier{&TestVerifier{}},
 	}
 
 	verifyParameters := e.VerifyParameters{
@@ -60,15 +91,19 @@ func TestVerifySubject_Verify_NoReferrers(t *testing.T) {
 }
 
 func TestVerifySubject_CanVerify_ExpectedResults(t *testing.T) {
+	testDigest := digest.FromString("test")
 	configPolicy := config.PolicyEnforcer{}
-	store := &TestStore{references: []ocispecs.ReferenceDescriptor{
+	store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{
 		{
 			ArtifactType: "test-type1",
 		},
 		{
 			ArtifactType: "test-type2",
+		}},
+		ResolveMap: map[string]digest.Digest{
+			"v1": testDigest,
 		},
-	}}
+	}
 	ver := &TestVerifier{
 		canVerify: func(at string) bool {
 			return at == "test-type1"
@@ -104,15 +139,19 @@ func TestVerifySubject_CanVerify_ExpectedResults(t *testing.T) {
 }
 
 func TestVerifySubject_VerifyFailures_ExpectedResults(t *testing.T) {
+	testDigest := digest.FromString("test")
 	configPolicy := config.PolicyEnforcer{}
-	store := &TestStore{references: []ocispecs.ReferenceDescriptor{
+	store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{
 		{
 			ArtifactType: "test-type1",
 		},
 		{
 			ArtifactType: "test-type2",
+		}},
+		ResolveMap: map[string]digest.Digest{
+			"v1": testDigest,
 		},
-	}}
+	}
 	ver := &TestVerifier{
 		canVerify: func(at string) bool {
 			return true
@@ -152,15 +191,19 @@ func TestVerifySubject_VerifyFailures_ExpectedResults(t *testing.T) {
 }
 
 func TestVerifySubject_VerifySuccess_ExpectedResults(t *testing.T) {
+	testDigest := digest.FromString("test")
 	configPolicy := config.PolicyEnforcer{}
-	store := &TestStore{references: []ocispecs.ReferenceDescriptor{
+	store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{
 		{
 			ArtifactType: "test-type1",
 		},
 		{
 			ArtifactType: "test-type2",
+		}},
+		ResolveMap: map[string]digest.Digest{
+			"v1": testDigest,
 		},
-	}}
+	}
 	ver := &TestVerifier{
 		canVerify: func(at string) bool {
 			return true
diff --git a/pkg/executor/core/testtypes.go b/pkg/executor/core/testtypes.go
index 473b5ce..4537657 100644
--- a/pkg/executor/core/testtypes.go
+++ b/pkg/executor/core/testtypes.go
@@ -22,35 +22,9 @@ import (
 	"github.com/deislabs/ratify/pkg/executor"
 	"github.com/deislabs/ratify/pkg/ocispecs"
 	"github.com/deislabs/ratify/pkg/referrerstore"
-	"github.com/deislabs/ratify/pkg/referrerstore/config"
 	"github.com/deislabs/ratify/pkg/verifier"
-	"github.com/opencontainers/go-digest"
 )
 
-type TestStore struct {
-	references []ocispecs.ReferenceDescriptor
-}
-
-func (s *TestStore) Name() string {
-	return "test-store"
-}
-
-func (s *TestStore) ListReferrers(ctx context.Context, subjectReference common.Reference, artifactTypes []string, nextToken string) (referrerstore.ListReferrersResult, error) {
-	return referrerstore.ListReferrersResult{Referrers: s.references}, nil
-}
-
-func (s *TestStore) GetBlobContent(ctx context.Context, subjectReference common.Reference, digest digest.Digest) ([]byte, error) {
-	return nil, nil
-}
-
-func (s *TestStore) GetReferenceManifest(ctx context.Context, subjectReference common.Reference, referenceDesc ocispecs.ReferenceDescriptor) (ocispecs.ReferenceManifest, error) {
-	return ocispecs.ReferenceManifest{}, nil
-}
-
-func (s *TestStore) GetConfig() *config.StoreConfig {
-	return &config.StoreConfig{}
-}
-
 type TestVerifier struct {
 	canVerify    func(artifactType string) bool
 	verifyResult func(artifactType string) bool
diff --git a/pkg/referrerstore/factory/factory_test.go b/pkg/referrerstore/factory/factory_test.go
index bcbb418..6c837b7 100644
--- a/pkg/referrerstore/factory/factory_test.go
+++ b/pkg/referrerstore/factory/factory_test.go
@@ -16,42 +16,18 @@ limitations under the License.
 package factory
 
 import (
-	"context"
 	"testing"
 
-	"github.com/deislabs/ratify/pkg/common"
-	"github.com/deislabs/ratify/pkg/ocispecs"
 	"github.com/deislabs/ratify/pkg/referrerstore"
 	"github.com/deislabs/ratify/pkg/referrerstore/config"
+	"github.com/deislabs/ratify/pkg/referrerstore/mocks"
 	"github.com/deislabs/ratify/pkg/referrerstore/plugin"
-	"github.com/opencontainers/go-digest"
 )
 
-type TestStore struct{}
 type TestStoreFactory struct{}
 
-func (s *TestStore) Name() string {
-	return "test-store"
-}
-
-func (s *TestStore) ListReferrers(ctx context.Context, subjectReference common.Reference, artifactTypes []string, nextToken string) (referrerstore.ListReferrersResult, error) {
-	return referrerstore.ListReferrersResult{}, nil
-}
-
-func (s *TestStore) GetBlobContent(ctx context.Context, subjectReference common.Reference, digest digest.Digest) ([]byte, error) {
-	return nil, nil
-}
-
-func (s *TestStore) GetReferenceManifest(ctx context.Context, subjectReference common.Reference, referenceDesc ocispecs.ReferenceDescriptor) (ocispecs.ReferenceManifest, error) {
-	return ocispecs.ReferenceManifest{}, nil
-}
-
-func (s *TestStore) GetConfig() *config.StoreConfig {
-	return nil
-}
-
 func (f *TestStoreFactory) Create(version string, storesConfig config.StorePluginConfig) (referrerstore.ReferrerStore, error) {
-	return &TestStore{}, nil
+	return &mocks.TestStore{}, nil
 }
 
 func TestCreateStoresFromConfig_BuiltInStores_ReturnsExpected(t *testing.T) {
diff --git a/pkg/referrerstore/plugin/plugin_test.go b/pkg/referrerstore/plugin/plugin_test.go
index 01f4699..7f67eec 100644
--- a/pkg/referrerstore/plugin/plugin_test.go
+++ b/pkg/referrerstore/plugin/plugin_test.go
@@ -17,12 +17,15 @@ package plugin
 
 import (
 	"context"
+	_ "crypto/sha256"
+	"fmt"
 	"strings"
 	"testing"
 
 	"github.com/deislabs/ratify/pkg/common"
 	"github.com/deislabs/ratify/pkg/ocispecs"
 	"github.com/deislabs/ratify/pkg/referrerstore/config"
+	"github.com/opencontainers/go-digest"
 )
 
 type TestExecutor struct {
@@ -281,3 +284,78 @@ func TestPluginMain_ListReferrers_InvokeExpected(t *testing.T) {
 		t.Fatalf("mismatch of result expected %s actual %v", "test-token", result)
 	}
 }
+
+func TestPluginMain_GetSubjectDescriptor_InvokeExpected(t *testing.T) {
+	testPlugin := "test-plugin"
+	testDigest := digest.FromString("test")
+	testExecutor := &TestExecutor{
+		find: func(plugin string, paths []string) (string, error) {
+			return "testpath", nil
+		},
+		execute: func(ctx context.Context, pluginPath string, cmdArgs []string, stdinData []byte, environ []string) ([]byte, error) {
+			if pluginPath != "testpath" {
+				t.Fatalf("mismatch in plugin path expected %s actual %s", "testpath", pluginPath)
+			}
+			if cmdArgs != nil {
+				t.Fatal("cmdArgs is expected to be nil")
+			}
+			stdData := string(stdinData[:])
+			if !strings.Contains(stdData, testPlugin) {
+				t.Fatalf("missing config data in stdin expected to have %s actual %s", "test-plugin", stdData)
+			}
+
+			commandCheck := false
+			versionCheck := false
+			subjectCheck := false
+			for _, env := range environ {
+				if strings.Contains(env, CommandEnvKey) && strings.Contains(env, GetSubjectDescriptor) {
+					commandCheck = true
+				} else if strings.Contains(env, VersionEnvKey) && strings.Contains(env, "1.0.0") {
+					versionCheck = true
+				} else if strings.Contains(env, SubjectEnvKey) && strings.Contains(env, "localhost") {
+					subjectCheck = true
+				}
+			}
+
+			if !commandCheck {
+				t.Fatalf("missing command env")
+			}
+
+			if !versionCheck {
+				t.Fatalf("missing version env")
+			}
+
+			if !subjectCheck {
+				t.Fatalf("missing subject env")
+			}
+
+			desc := fmt.Sprintf(`{"digest":"%s"}`, testDigest.String())
+			return []byte(desc), nil
+		},
+	}
+
+	var rawConfig config.StorePluginConfig
+	rawConfig = map[string]interface{}{
+		testPlugin: StorePlugin{
+			name: testPlugin,
+		},
+	}
+	storePlugin := &StorePlugin{
+		executor:  testExecutor,
+		name:      testPlugin,
+		version:   "1.0.0",
+		rawConfig: rawConfig,
+	}
+
+	subject := common.Reference{
+		Original: "localhost",
+	}
+	result, err := storePlugin.GetSubjectDescriptor(context.Background(), subject)
+	if err != nil {
+		t.Fatalf("plugin execution failed %v", err)
+	}
+
+	if result.Digest != testDigest {
+		t.Fatalf("mismatch of result expected %s actual %v", testDigest, result)
+	}
+}
diff --git a/pkg/referrerstore/plugin/skel/skel_test.go b/pkg/referrerstore/plugin/skel/skel_test.go
index f919fb4..35d2b5b 100644
--- a/pkg/referrerstore/plugin/skel/skel_test.go
+++ b/pkg/referrerstore/plugin/skel/skel_test.go
@@ -27,6 +27,7 @@ import (
 	"github.com/deislabs/ratify/pkg/referrerstore/plugin"
 	"github.com/deislabs/ratify/pkg/referrerstore/types"
 	"github.com/opencontainers/go-digest"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
 func TestPluginMain_GetBlobContent_ReturnsExpected(t *testing.T) {
@@ -50,7 +51,7 @@ func TestPluginMain_GetBlobContent_ReturnsExpected(t *testing.T) {
 		Stderr:     stderr,
 	}
 
-	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err != nil {
 		t.Fatalf("plugin execution failed %v", err)
 	}
@@ -85,7 +86,7 @@ func TestPluginMain_GetReferenceManifest_ReturnsExpected(t *testing.T) {
 		Stderr:     stderr,
 	}
 
-	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, nil, getReferenceManifest, []string{"1.0.0"})
+	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, nil, getReferenceManifest, nil, []string{"1.0.0"})
 	if err != nil {
 		t.Fatalf("plugin execution failed %v", err)
 	}
@@ -125,7 +126,7 @@ func TestPluginMain_ListReferrers_ReturnsExpected(t *testing.T) {
 		Stderr:     stderr,
 	}
 
-	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", listReferrers, nil, nil, []string{"1.0.0"})
+	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", listReferrers, nil, nil, nil, []string{"1.0.0"})
 	if err != nil {
 		t.Fatalf("plugin execution failed %v", err)
 	}
@@ -136,6 +137,39 @@ func TestPluginMain_ListReferrers_ReturnsExpected(t *testing.T) {
 	}
 }
 
+func TestPluginMain_GetSubjectDesc_ReturnsExpected(t *testing.T) {
+	testDigest := digest.FromString("test")
+	getSubjectDesc := func(args *CmdArgs, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error) {
+		return &ocispecs.SubjectDescriptor{Descriptor: v1.Descriptor{Digest: testDigest}}, nil
+	}
+
+	environment := map[string]string{
+		plugin.CommandEnvKey: plugin.GetSubjectDescriptor,
+		plugin.VersionEnvKey: "1.0.0",
+		plugin.SubjectEnvKey: "localhost:5000/net-monitor:v1",
+	}
+
+	stdinData := `{ "name":"skel-test-case", "some": "config" }`
+	stdout := &bytes.Buffer{}
+	stderr := &bytes.Buffer{}
+	pluginContext := &pcontext{
+		GetEnviron: func(key string) string { return environment[key] },
+		Stdin:      strings.NewReader(stdinData),
+		Stdout:     stdout,
+		Stderr:     stderr,
+	}
+
+	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, nil, nil, getSubjectDesc, []string{"1.0.0"})
+	if err != nil {
+		t.Fatalf("plugin execution failed %v", err)
+	}
+
+	out := fmt.Sprintf("%s", stdout)
+	if !strings.Contains(out, testDigest.String()) {
+		t.Fatalf("plugin execution failed. expected %v actual %v", testDigest.String(), out)
+	}
+}
+
 func TestPluginMain_ErrorCases(t *testing.T) {
 	getBlobContent := func(args *CmdArgs, subjectReference common.Reference, digest digest.Digest) ([]byte, error) {
 		return nil, fmt.Errorf("simulated error")
@@ -156,7 +190,7 @@ func TestPluginMain_ErrorCases(t *testing.T) {
 		Stderr:     stderr,
 	}
 
-	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrMissingEnvironmentVariables {
 		t.Fatalf("plugin execution expected to fail with error code %d", types.ErrMissingEnvironmentVariables)
 	}
@@ -164,14 +198,14 @@ func TestPluginMain_ErrorCases(t *testing.T) {
 	environment[plugin.VersionEnvKey] = "1.0.0"
 	environment[plugin.SubjectEnvKey] = "localhost&300"
 
-	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrArgsParsingFailure {
 		t.Fatalf("plugin execution expected to fail with error code %d for invalid subject", types.ErrArgsParsingFailure)
 	}
 
 	environment[plugin.SubjectEnvKey] = "localhost:5000/net-monitor:v1@sha256:a0fc570a245b09ed752c42d600ee3bb5b4f77bbd70d8898780b7ab43454530eb"
 	environment[plugin.VersionEnvKey] = "2.0.0"
-	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrVersionNotSupported {
 		t.Fatalf("plugin execution expected to fail with error code %d for unsupported version", types.ErrVersionNotSupported)
 	}
@@ -180,14 +214,14 @@ func TestPluginMain_ErrorCases(t *testing.T) {
 
 	stdinData = ` "name":"skel-test-case", "some": "config" }`
 	pluginContext.Stdin = strings.NewReader(stdinData)
-	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrConfigParsingFailure {
 		t.Fatalf("plugin execution expected to fail with error code %d for invalid config", types.ErrConfigParsingFailure)
 	}
 
 	stdinData = ` {"some": "config" }`
 	pluginContext.Stdin = strings.NewReader(stdinData)
-	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrInvalidStoreConfig {
 		t.Fatalf("plugin execution expected to fail with error code %d for missing store name", types.ErrInvalidStoreConfig)
 	}
@@ -195,7 +229,7 @@ func TestPluginMain_ErrorCases(t *testing.T) {
 	environment[plugin.CommandEnvKey] = "unknown"
 	stdinData = ` {"name":"skel-test-case", "some": "config" }`
 	pluginContext.Stdin = strings.NewReader(stdinData)
-	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrUnknownCommand {
 		t.Fatalf("plugin execution expected to fail with error code %d for invalid command", types.ErrUnknownCommand)
 	}
@@ -203,7 +237,7 @@ func TestPluginMain_ErrorCases(t *testing.T) {
 	environment[plugin.CommandEnvKey] = plugin.GetBlobContentCommand
 	stdinData = ` {"name":"skel-test-case", "some": "config" }`
 	pluginContext.Stdin = strings.NewReader(stdinData)
-	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrPluginCmdFailure {
 		t.Fatalf("plugin execution expected to fail with error code %d for cmd failure", types.ErrPluginCmdFailure)
 	}
@@ -230,7 +264,7 @@ func TestPluginMain_GetBlobContent_ErrorCases(t *testing.T) {
 		Stderr:     stderr,
 	}
 
-	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrArgsParsingFailure {
 		t.Fatalf("plugin execution expected to fail with error code %d for invalid arg", types.ErrArgsParsingFailure)
 	}
@@ -238,7 +272,7 @@ func TestPluginMain_GetBlobContent_ErrorCases(t *testing.T) {
 	stdinData = ` {"name":"skel-test-case", "some": "config" }`
 	pluginContext.Stdin = strings.NewReader(stdinData)
 	environment[plugin.ArgsEnvKey] = "digest=sha256a0fc570a245b09ed752c42d600ee3bb5b4f77bbd70d8898780b7ab43454530eb"
-	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, []string{"1.0.0"})
+	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrArgsParsingFailure {
 		t.Fatalf("plugin execution expected to fail with error code %d for invalid digest", types.ErrArgsParsingFailure)
 	}
@@ -273,7 +307,7 @@ func TestPluginMain_ListReferrers_ErrorCases(t *testing.T) {
 		Stderr:     stderr,
 	}
 
-	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", listReferrers, nil, nil, []string{"1.0.0"})
+	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", listReferrers, nil, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrArgsParsingFailure {
 		t.Fatalf("plugin execution expected to fail with error code %d for invalid arg", types.ErrArgsParsingFailure)
 	}
diff --git a/pkg/referrerstore/utils/utils_test.go b/pkg/referrerstore/utils/utils_test.go
new file mode 100644
index 0000000..dc46e0f
--- /dev/null
+++ b/pkg/referrerstore/utils/utils_test.go
@@ -0,0 +1,65 @@
+/*
+Copyright The Ratify Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package utils
+
+import (
+	"context"
+	"testing"
+
+	"github.com/deislabs/ratify/pkg/referrerstore"
+	"github.com/deislabs/ratify/pkg/referrerstore/mocks"
+	"github.com/deislabs/ratify/pkg/utils"
+	"github.com/opencontainers/go-digest"
+)
+
+func TestResolveSubjectDescriptor_Success(t *testing.T) {
+	testDigest := digest.FromString("test")
+	store1 := &mocks.TestStore{}
+	store2 := &mocks.TestStore{
+		ResolveMap: map[string]digest.Digest{
+			"v1": testDigest,
+		},
+	}
+	subjectReference, err := utils.ParseSubjectReference("localhost:5000/net-monitor:v1")
+	if err != nil {
+		t.Fatalf("failed to parse the subject %v", err)
+	}
+
+	result, err := ResolveSubjectDescriptor(context.Background(), &[]referrerstore.ReferrerStore{store1, store2}, subjectReference)
+
+	if err != nil {
+		t.Fatalf("failed to get the subject descriptor %v", err)
+	}
+
+	if result.Digest != testDigest {
+		t.Fatalf("digest mismatch expected %v actual %v", testDigest, result.Digest)
+	}
+}
+
+func TestResolveSubjectDescriptor_Failure(t *testing.T) {
+	store1 := &mocks.TestStore{}
+	store2 := &mocks.TestStore{}
+	subjectReference, err := utils.ParseSubjectReference("localhost:5000/net-monitor:v1")
+	if err != nil {
+		t.Fatalf("failed to parse the subject %v", err)
+	}
+
+	_, err = ResolveSubjectDescriptor(context.Background(), &[]referrerstore.ReferrerStore{store1, store2}, subjectReference)
+
+	if err == nil {
+		t.Fatalf("expected resolve to fail but didnot get any error")
+	}
+}
diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go
index 722f42a..02f1019 100644
--- a/pkg/utils/utils_test.go
+++ b/pkg/utils/utils_test.go
@@ -68,9 +68,12 @@ func TestParseSubjectReference_ReturnsExpected(t *testing.T) {
 			expectedErrMsg: "failed to parse subject reference",
 		},
 		{
-			input:          "localhost:5000/net-monitor:v1",
-			output:         common.Reference{},
-			expectedErrMsg: "failed to parse subject reference digest",
+			input: "localhost:5000/net-monitor:v1",
+			output: common.Reference{
+				Path:   "localhost:5000/net-monitor",
+				Tag:    "v1",
+				Digest: "",
+			},
 		},
 		{
 			input:          "localhost:5000/net&monitor:v1",
diff --git a/pkg/verifier/plugin/plugin_test.go b/pkg/verifier/plugin/plugin_test.go
index 1fcf7de..28c9dda 100644
--- a/pkg/verifier/plugin/plugin_test.go
+++ b/pkg/verifier/plugin/plugin_test.go
@@ -22,6 +22,7 @@ import (
 
 	"github.com/deislabs/ratify/pkg/common"
 	"github.com/deislabs/ratify/pkg/ocispecs"
+	sm "github.com/deislabs/ratify/pkg/referrerstore/mocks"
 	"github.com/deislabs/ratify/pkg/verifier/config"
 	"github.com/deislabs/ratify/pkg/verifier/mocks"
 )
@@ -129,7 +130,7 @@ func TestVerify_NoNestedReferences_Expected(t *testing.T) {
 		ArtifactType: "test-type",
 	}
 
-	result, err := verifierPlugin.Verify(context.Background(), subject, ref, &mocks.TestStore{}, &mocks.TestExecutor{})
+	result, err := verifierPlugin.Verify(context.Background(), subject, ref, &sm.TestStore{}, &mocks.TestExecutor{})
 
 	if err != nil {
 		t.Fatalf("plugin execution failed %v", err)
@@ -163,7 +164,7 @@ func TestVerify_NestedReferences_Verify_Failed(t *testing.T) {
 		ArtifactType: "test-type",
 	}
 
-	result, err := verifierPlugin.Verify(context.Background(), subject, ref, &mocks.TestStore{}, &mocks.TestExecutor{VerifySuccess: false})
+	result, err := verifierPlugin.Verify(context.Background(), subject, ref, &sm.TestStore{}, &mocks.TestExecutor{VerifySuccess: false})
 
 	if err != nil {
 		t.Fatalf("plugin execution failed %v", err)
@@ -246,7 +247,7 @@ func TestVerify_NestedReferences_Verify_Success(t *testing.T) {
 		ArtifactType: "test-type",
 	}
 
-	result, err := verifierPlugin.Verify(context.Background(), subject, ref, &mocks.TestStore{}, &mocks.TestExecutor{VerifySuccess: true})
+	result, err := verifierPlugin.Verify(context.Background(), subject, ref, &sm.TestStore{}, &mocks.TestExecutor{VerifySuccess: true})
 
 	if err != nil {
 		t.Fatalf("plugin execution failed %v", err)