diff --git a/cmd/rekor-cli/app/pflag_groups.go b/cmd/rekor-cli/app/pflag_groups.go
index a005e26c37b6f510f52b4a859ed43294bd4a49e2..c94c140e3cf4e5080272d911df220ab9b2fc13fe 100644
--- a/cmd/rekor-cli/app/pflag_groups.go
+++ b/cmd/rekor-cli/app/pflag_groups.go
@@ -109,11 +109,11 @@ func validateArtifactPFlags(uuidValid, indexValid bool) error {
 	}
 
 	// if neither --entry or --artifact were given, then a reference to a uuid or index is needed
-	if viper.GetString("entry") == "" && viper.GetString("artifact") == "" {
+	if viper.GetString("entry") == "" && viper.GetString("artifact") == "" && viper.GetString("artifact-hash") == "" {
 		if (uuidGiven && uuidValid) || (indexGiven && indexValid) {
 			return nil
 		}
-		return errors.New("either 'entry' or 'artifact' must be specified")
+		return errors.New("either 'entry' or 'artifact' or 'artifact-hash' must be specified")
 	}
 
 	return nil
diff --git a/cmd/rekor-cli/app/root.go b/cmd/rekor-cli/app/root.go
index 3968cb69955a04cfb40f5375f6371f381c4bb992..d05de5616272327f671fec6d1b3567cc3f2169cd 100644
--- a/cmd/rekor-cli/app/root.go
+++ b/cmd/rekor-cli/app/root.go
@@ -29,6 +29,7 @@ import (
 
 	// these imports are to call the packages' init methods
 	_ "github.com/sigstore/rekor/pkg/types/alpine/v0.0.1"
+	_ "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1"
 	_ "github.com/sigstore/rekor/pkg/types/helm/v0.0.1"
 	_ "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1"
 	_ "github.com/sigstore/rekor/pkg/types/jar/v0.0.1"
diff --git a/cmd/rekor-server/app/serve.go b/cmd/rekor-server/app/serve.go
index 3e963aecdfb01063a72c67028b7c056cc5a5d7de..ae13a63677b9b0a5d13850564d67a9456ac1a459 100644
--- a/cmd/rekor-server/app/serve.go
+++ b/cmd/rekor-server/app/serve.go
@@ -30,6 +30,8 @@ import (
 	"github.com/sigstore/rekor/pkg/log"
 	"github.com/sigstore/rekor/pkg/types/alpine"
 	alpine_v001 "github.com/sigstore/rekor/pkg/types/alpine/v0.0.1"
+	hashedrekord "github.com/sigstore/rekor/pkg/types/hashedrekord"
+	hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1"
 	"github.com/sigstore/rekor/pkg/types/helm"
 	helm_v001 "github.com/sigstore/rekor/pkg/types/helm/v0.0.1"
 	"github.com/sigstore/rekor/pkg/types/intoto"
@@ -80,14 +82,15 @@ var serveCmd = &cobra.Command{
 
 		// these trigger loading of package and therefore init() methods to run
 		pluggableTypeMap := map[string]string{
-			rekord.KIND:  rekord_v001.APIVERSION,
-			rpm.KIND:     rpm_v001.APIVERSION,
-			jar.KIND:     jar_v001.APIVERSION,
-			intoto.KIND:  intoto_v001.APIVERSION,
-			rfc3161.KIND: rfc3161_v001.APIVERSION,
-			alpine.KIND:  alpine_v001.APIVERSION,
-			helm.KIND:    helm_v001.APIVERSION,
-			tuf.KIND:     tuf_v001.APIVERSION,
+			rekord.KIND:       rekord_v001.APIVERSION,
+			rpm.KIND:          rpm_v001.APIVERSION,
+			jar.KIND:          jar_v001.APIVERSION,
+			intoto.KIND:       intoto_v001.APIVERSION,
+			rfc3161.KIND:      rfc3161_v001.APIVERSION,
+			alpine.KIND:       alpine_v001.APIVERSION,
+			helm.KIND:         helm_v001.APIVERSION,
+			tuf.KIND:          tuf_v001.APIVERSION,
+			hashedrekord.KIND: hashedrekord_v001.APIVERSION,
 		}
 
 		for k, v := range pluggableTypeMap {
diff --git a/openapi.yaml b/openapi.yaml
index 88e1ff7eacd9e34b1212d09efce6c60c259b499c..63c64b20c0dd6975b65253953d50fab3cc57f6d6 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -312,6 +312,23 @@ definitions:
         - spec
       additionalProperties: false
 
+  hashedrekord:
+    type: object
+    description: Hashed Rekord object
+    allOf:
+    - $ref: '#/definitions/ProposedEntry'
+    - properties:
+        apiVersion:
+          type: string
+          pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
+        spec:
+          type: object
+          $ref: 'pkg/types/hashedrekord/hashedrekord_schema.json'
+      required:
+        - apiVersion
+        - spec
+      additionalProperties: false
+
   rpm:
     type: object
     description: RPM package
diff --git a/pkg/generated/models/hashedrekord.go b/pkg/generated/models/hashedrekord.go
new file mode 100644
index 0000000000000000000000000000000000000000..b3e1f8a3bda4a6aec4669e6b7e24cea90a454a66
--- /dev/null
+++ b/pkg/generated/models/hashedrekord.go
@@ -0,0 +1,210 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+//
+// Copyright 2021 The Sigstore 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 models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+
+	"github.com/go-openapi/errors"
+	"github.com/go-openapi/strfmt"
+	"github.com/go-openapi/swag"
+	"github.com/go-openapi/validate"
+)
+
+// Hashedrekord Hashed Rekord object
+//
+// swagger:model hashedrekord
+type Hashedrekord struct {
+
+	// api version
+	// Required: true
+	// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
+	APIVersion *string `json:"apiVersion"`
+
+	// spec
+	// Required: true
+	Spec HashedrekordSchema `json:"spec"`
+}
+
+// Kind gets the kind of this subtype
+func (m *Hashedrekord) Kind() string {
+	return "hashedrekord"
+}
+
+// SetKind sets the kind of this subtype
+func (m *Hashedrekord) SetKind(val string) {
+}
+
+// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
+func (m *Hashedrekord) UnmarshalJSON(raw []byte) error {
+	var data struct {
+
+		// api version
+		// Required: true
+		// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
+		APIVersion *string `json:"apiVersion"`
+
+		// spec
+		// Required: true
+		Spec HashedrekordSchema `json:"spec"`
+	}
+	buf := bytes.NewBuffer(raw)
+	dec := json.NewDecoder(buf)
+	dec.UseNumber()
+
+	if err := dec.Decode(&data); err != nil {
+		return err
+	}
+
+	var base struct {
+		/* Just the base type fields. Used for unmashalling polymorphic types.*/
+
+		Kind string `json:"kind"`
+	}
+	buf = bytes.NewBuffer(raw)
+	dec = json.NewDecoder(buf)
+	dec.UseNumber()
+
+	if err := dec.Decode(&base); err != nil {
+		return err
+	}
+
+	var result Hashedrekord
+
+	if base.Kind != result.Kind() {
+		/* Not the type we're looking for. */
+		return errors.New(422, "invalid kind value: %q", base.Kind)
+	}
+
+	result.APIVersion = data.APIVersion
+	result.Spec = data.Spec
+
+	*m = result
+
+	return nil
+}
+
+// MarshalJSON marshals this object with a polymorphic type to a JSON structure
+func (m Hashedrekord) MarshalJSON() ([]byte, error) {
+	var b1, b2, b3 []byte
+	var err error
+	b1, err = json.Marshal(struct {
+
+		// api version
+		// Required: true
+		// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
+		APIVersion *string `json:"apiVersion"`
+
+		// spec
+		// Required: true
+		Spec HashedrekordSchema `json:"spec"`
+	}{
+
+		APIVersion: m.APIVersion,
+
+		Spec: m.Spec,
+	})
+	if err != nil {
+		return nil, err
+	}
+	b2, err = json.Marshal(struct {
+		Kind string `json:"kind"`
+	}{
+
+		Kind: m.Kind(),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return swag.ConcatJSON(b1, b2, b3), nil
+}
+
+// Validate validates this hashedrekord
+func (m *Hashedrekord) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validateAPIVersion(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validateSpec(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *Hashedrekord) validateAPIVersion(formats strfmt.Registry) error {
+
+	if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
+		return err
+	}
+
+	if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (m *Hashedrekord) validateSpec(formats strfmt.Registry) error {
+
+	if m.Spec == nil {
+		return errors.Required("spec", "body", nil)
+	}
+
+	return nil
+}
+
+// ContextValidate validate this hashedrekord based on the context it is used
+func (m *Hashedrekord) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+	var res []error
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *Hashedrekord) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *Hashedrekord) UnmarshalBinary(b []byte) error {
+	var res Hashedrekord
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
diff --git a/pkg/generated/models/hashedrekord_schema.go b/pkg/generated/models/hashedrekord_schema.go
new file mode 100644
index 0000000000000000000000000000000000000000..49d5831f81221eff969eb04f03f3fdc0aa48ab8c
--- /dev/null
+++ b/pkg/generated/models/hashedrekord_schema.go
@@ -0,0 +1,29 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+//
+// Copyright 2021 The Sigstore 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 models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+// HashedrekordSchema Rekor Schema
+//
+// Schema for Rekord objects
+//
+// swagger:model hashedrekordSchema
+type HashedrekordSchema interface{}
diff --git a/pkg/generated/models/hashedrekord_v001_schema.go b/pkg/generated/models/hashedrekord_v001_schema.go
new file mode 100644
index 0000000000000000000000000000000000000000..50fd24827ea9de329fe59289b961c5e74a7593c1
--- /dev/null
+++ b/pkg/generated/models/hashedrekord_v001_schema.go
@@ -0,0 +1,501 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+//
+// Copyright 2021 The Sigstore 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 models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+	"context"
+	"encoding/json"
+
+	"github.com/go-openapi/errors"
+	"github.com/go-openapi/strfmt"
+	"github.com/go-openapi/swag"
+	"github.com/go-openapi/validate"
+)
+
+// HashedrekordV001Schema Hashed Rekor v0.0.1 Schema
+//
+// Schema for Hashed Rekord object
+//
+// swagger:model hashedrekordV001Schema
+type HashedrekordV001Schema struct {
+
+	// data
+	// Required: true
+	Data *HashedrekordV001SchemaData `json:"data"`
+
+	// signature
+	// Required: true
+	Signature *HashedrekordV001SchemaSignature `json:"signature"`
+}
+
+// Validate validates this hashedrekord v001 schema
+func (m *HashedrekordV001Schema) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validateData(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validateSignature(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *HashedrekordV001Schema) validateData(formats strfmt.Registry) error {
+
+	if err := validate.Required("data", "body", m.Data); err != nil {
+		return err
+	}
+
+	if m.Data != nil {
+		if err := m.Data.Validate(formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("data")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("data")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (m *HashedrekordV001Schema) validateSignature(formats strfmt.Registry) error {
+
+	if err := validate.Required("signature", "body", m.Signature); err != nil {
+		return err
+	}
+
+	if m.Signature != nil {
+		if err := m.Signature.Validate(formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("signature")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("signature")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+// ContextValidate validate this hashedrekord v001 schema based on the context it is used
+func (m *HashedrekordV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.contextValidateData(ctx, formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.contextValidateSignature(ctx, formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *HashedrekordV001Schema) contextValidateData(ctx context.Context, formats strfmt.Registry) error {
+
+	if m.Data != nil {
+		if err := m.Data.ContextValidate(ctx, formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("data")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("data")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (m *HashedrekordV001Schema) contextValidateSignature(ctx context.Context, formats strfmt.Registry) error {
+
+	if m.Signature != nil {
+		if err := m.Signature.ContextValidate(ctx, formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("signature")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("signature")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *HashedrekordV001Schema) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *HashedrekordV001Schema) UnmarshalBinary(b []byte) error {
+	var res HashedrekordV001Schema
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
+
+// HashedrekordV001SchemaData Information about the content associated with the entry
+//
+// swagger:model HashedrekordV001SchemaData
+type HashedrekordV001SchemaData struct {
+
+	// hash
+	Hash *HashedrekordV001SchemaDataHash `json:"hash,omitempty"`
+}
+
+// Validate validates this hashedrekord v001 schema data
+func (m *HashedrekordV001SchemaData) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validateHash(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *HashedrekordV001SchemaData) validateHash(formats strfmt.Registry) error {
+	if swag.IsZero(m.Hash) { // not required
+		return nil
+	}
+
+	if m.Hash != nil {
+		if err := m.Hash.Validate(formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("data" + "." + "hash")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("data" + "." + "hash")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+// ContextValidate validate this hashedrekord v001 schema data based on the context it is used
+func (m *HashedrekordV001SchemaData) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.contextValidateHash(ctx, formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *HashedrekordV001SchemaData) contextValidateHash(ctx context.Context, formats strfmt.Registry) error {
+
+	if m.Hash != nil {
+		if err := m.Hash.ContextValidate(ctx, formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("data" + "." + "hash")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("data" + "." + "hash")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *HashedrekordV001SchemaData) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *HashedrekordV001SchemaData) UnmarshalBinary(b []byte) error {
+	var res HashedrekordV001SchemaData
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
+
+// HashedrekordV001SchemaDataHash Specifies the hash algorithm and value for the content
+//
+// swagger:model HashedrekordV001SchemaDataHash
+type HashedrekordV001SchemaDataHash struct {
+
+	// The hashing function used to compute the hash value
+	// Required: true
+	// Enum: [sha256]
+	Algorithm *string `json:"algorithm"`
+
+	// The hash value for the content
+	// Required: true
+	Value *string `json:"value"`
+}
+
+// Validate validates this hashedrekord v001 schema data hash
+func (m *HashedrekordV001SchemaDataHash) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validateAlgorithm(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validateValue(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+var hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum []interface{}
+
+func init() {
+	var res []string
+	if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
+		panic(err)
+	}
+	for _, v := range res {
+		hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum = append(hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum, v)
+	}
+}
+
+const (
+
+	// HashedrekordV001SchemaDataHashAlgorithmSha256 captures enum value "sha256"
+	HashedrekordV001SchemaDataHashAlgorithmSha256 string = "sha256"
+)
+
+// prop value enum
+func (m *HashedrekordV001SchemaDataHash) validateAlgorithmEnum(path, location string, value string) error {
+	if err := validate.EnumCase(path, location, value, hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum, true); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (m *HashedrekordV001SchemaDataHash) validateAlgorithm(formats strfmt.Registry) error {
+
+	if err := validate.Required("data"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil {
+		return err
+	}
+
+	// value enum
+	if err := m.validateAlgorithmEnum("data"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (m *HashedrekordV001SchemaDataHash) validateValue(formats strfmt.Registry) error {
+
+	if err := validate.Required("data"+"."+"hash"+"."+"value", "body", m.Value); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// ContextValidate validates this hashedrekord v001 schema data hash based on context it is used
+func (m *HashedrekordV001SchemaDataHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *HashedrekordV001SchemaDataHash) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *HashedrekordV001SchemaDataHash) UnmarshalBinary(b []byte) error {
+	var res HashedrekordV001SchemaDataHash
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
+
+// HashedrekordV001SchemaSignature Information about the detached signature associated with the entry
+//
+// swagger:model HashedrekordV001SchemaSignature
+type HashedrekordV001SchemaSignature struct {
+
+	// Specifies the content of the signature inline within the document
+	// Format: byte
+	Content strfmt.Base64 `json:"content,omitempty"`
+
+	// public key
+	PublicKey *HashedrekordV001SchemaSignaturePublicKey `json:"publicKey,omitempty"`
+}
+
+// Validate validates this hashedrekord v001 schema signature
+func (m *HashedrekordV001SchemaSignature) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validatePublicKey(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *HashedrekordV001SchemaSignature) validatePublicKey(formats strfmt.Registry) error {
+	if swag.IsZero(m.PublicKey) { // not required
+		return nil
+	}
+
+	if m.PublicKey != nil {
+		if err := m.PublicKey.Validate(formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("signature" + "." + "publicKey")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("signature" + "." + "publicKey")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+// ContextValidate validate this hashedrekord v001 schema signature based on the context it is used
+func (m *HashedrekordV001SchemaSignature) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.contextValidatePublicKey(ctx, formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *HashedrekordV001SchemaSignature) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error {
+
+	if m.PublicKey != nil {
+		if err := m.PublicKey.ContextValidate(ctx, formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("signature" + "." + "publicKey")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("signature" + "." + "publicKey")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *HashedrekordV001SchemaSignature) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *HashedrekordV001SchemaSignature) UnmarshalBinary(b []byte) error {
+	var res HashedrekordV001SchemaSignature
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
+
+// HashedrekordV001SchemaSignaturePublicKey The public key that can verify the signature
+//
+// swagger:model HashedrekordV001SchemaSignaturePublicKey
+type HashedrekordV001SchemaSignaturePublicKey struct {
+
+	// Specifies the content of the public key inline within the document
+	// Format: byte
+	Content strfmt.Base64 `json:"content,omitempty"`
+}
+
+// Validate validates this hashedrekord v001 schema signature public key
+func (m *HashedrekordV001SchemaSignaturePublicKey) Validate(formats strfmt.Registry) error {
+	return nil
+}
+
+// ContextValidate validates this hashedrekord v001 schema signature public key based on context it is used
+func (m *HashedrekordV001SchemaSignaturePublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *HashedrekordV001SchemaSignaturePublicKey) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *HashedrekordV001SchemaSignaturePublicKey) UnmarshalBinary(b []byte) error {
+	var res HashedrekordV001SchemaSignaturePublicKey
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
diff --git a/pkg/generated/models/proposed_entry.go b/pkg/generated/models/proposed_entry.go
index 20a1c94a58d1e9814da21914cbf621d1dba0f0c4..a9c360586377825b2a39f14334f510be56f0f31c 100644
--- a/pkg/generated/models/proposed_entry.go
+++ b/pkg/generated/models/proposed_entry.go
@@ -121,6 +121,12 @@ func unmarshalProposedEntry(data []byte, consumer runtime.Consumer) (ProposedEnt
 			return nil, err
 		}
 		return &result, nil
+	case "hashedrekord":
+		var result Hashedrekord
+		if err := consumer.Consume(buf2, &result); err != nil {
+			return nil, err
+		}
+		return &result, nil
 	case "helm":
 		var result Helm
 		if err := consumer.Consume(buf2, &result); err != nil {
diff --git a/pkg/generated/restapi/embedded_spec.go b/pkg/generated/restapi/embedded_spec.go
index de483f663faa629556657e1df8588d4db97ff46e..7441c7920489a8575e060fdfdc9544705514288b 100644
--- a/pkg/generated/restapi/embedded_spec.go
+++ b/pkg/generated/restapi/embedded_spec.go
@@ -664,6 +664,32 @@ func init() {
         }
       ]
     },
+    "hashedrekord": {
+      "description": "Hashed Rekord object",
+      "type": "object",
+      "allOf": [
+        {
+          "$ref": "#/definitions/ProposedEntry"
+        },
+        {
+          "required": [
+            "apiVersion",
+            "spec"
+          ],
+          "properties": {
+            "apiVersion": {
+              "type": "string",
+              "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
+            },
+            "spec": {
+              "type": "object",
+              "$ref": "pkg/types/hashedrekord/hashedrekord_schema.json"
+            }
+          },
+          "additionalProperties": false
+        }
+      ]
+    },
     "helm": {
       "description": "Helm chart",
       "type": "object",
@@ -1457,6 +1483,87 @@ func init() {
         }
       }
     },
+    "HashedrekordV001SchemaData": {
+      "description": "Information about the content associated with the entry",
+      "type": "object",
+      "properties": {
+        "hash": {
+          "description": "Specifies the hash algorithm and value for the content",
+          "type": "object",
+          "required": [
+            "algorithm",
+            "value"
+          ],
+          "properties": {
+            "algorithm": {
+              "description": "The hashing function used to compute the hash value",
+              "type": "string",
+              "enum": [
+                "sha256"
+              ]
+            },
+            "value": {
+              "description": "The hash value for the content",
+              "type": "string"
+            }
+          }
+        }
+      }
+    },
+    "HashedrekordV001SchemaDataHash": {
+      "description": "Specifies the hash algorithm and value for the content",
+      "type": "object",
+      "required": [
+        "algorithm",
+        "value"
+      ],
+      "properties": {
+        "algorithm": {
+          "description": "The hashing function used to compute the hash value",
+          "type": "string",
+          "enum": [
+            "sha256"
+          ]
+        },
+        "value": {
+          "description": "The hash value for the content",
+          "type": "string"
+        }
+      }
+    },
+    "HashedrekordV001SchemaSignature": {
+      "description": "Information about the detached signature associated with the entry",
+      "type": "object",
+      "properties": {
+        "content": {
+          "description": "Specifies the content of the signature inline within the document",
+          "type": "string",
+          "format": "byte"
+        },
+        "publicKey": {
+          "description": "The public key that can verify the signature",
+          "type": "object",
+          "properties": {
+            "content": {
+              "description": "Specifies the content of the public key inline within the document",
+              "type": "string",
+              "format": "byte"
+            }
+          }
+        }
+      }
+    },
+    "HashedrekordV001SchemaSignaturePublicKey": {
+      "description": "The public key that can verify the signature",
+      "type": "object",
+      "properties": {
+        "content": {
+          "description": "Specifies the content of the public key inline within the document",
+          "type": "string",
+          "format": "byte"
+        }
+      }
+    },
     "HelmV001SchemaChart": {
       "description": "Information about the Helm chart associated with the entry",
       "type": "object",
@@ -2535,6 +2642,105 @@ func init() {
       "$schema": "http://json-schema.org/draft-07/schema",
       "$id": "http://rekor.sigstore.dev/types/alpine/alpine_v0_0_1_schema.json"
     },
+    "hashedrekord": {
+      "description": "Hashed Rekord object",
+      "type": "object",
+      "allOf": [
+        {
+          "$ref": "#/definitions/ProposedEntry"
+        },
+        {
+          "required": [
+            "apiVersion",
+            "spec"
+          ],
+          "properties": {
+            "apiVersion": {
+              "type": "string",
+              "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
+            },
+            "spec": {
+              "$ref": "#/definitions/hashedrekordSchema"
+            }
+          },
+          "additionalProperties": false
+        }
+      ]
+    },
+    "hashedrekordSchema": {
+      "description": "Schema for Rekord objects",
+      "type": "object",
+      "title": "Rekor Schema",
+      "oneOf": [
+        {
+          "$ref": "#/definitions/hashedrekordV001Schema"
+        }
+      ],
+      "$schema": "http://json-schema.org/draft-07/schema",
+      "$id": "http://rekor.sigstore.dev/types/hashedrekord/hasehedrekord_schema.json"
+    },
+    "hashedrekordV001Schema": {
+      "description": "Schema for Hashed Rekord object",
+      "type": "object",
+      "title": "Hashed Rekor v0.0.1 Schema",
+      "required": [
+        "signature",
+        "data"
+      ],
+      "properties": {
+        "data": {
+          "description": "Information about the content associated with the entry",
+          "type": "object",
+          "properties": {
+            "hash": {
+              "description": "Specifies the hash algorithm and value for the content",
+              "type": "object",
+              "required": [
+                "algorithm",
+                "value"
+              ],
+              "properties": {
+                "algorithm": {
+                  "description": "The hashing function used to compute the hash value",
+                  "type": "string",
+                  "enum": [
+                    "sha256"
+                  ]
+                },
+                "value": {
+                  "description": "The hash value for the content",
+                  "type": "string"
+                }
+              }
+            }
+          }
+        },
+        "signature": {
+          "description": "Information about the detached signature associated with the entry",
+          "type": "object",
+          "properties": {
+            "content": {
+              "description": "Specifies the content of the signature inline within the document",
+              "type": "string",
+              "format": "byte"
+            },
+            "publicKey": {
+              "description": "The public key that can verify the signature",
+              "type": "object",
+              "properties": {
+                "content": {
+                  "description": "Specifies the content of the public key inline within the document",
+                  "type": "string",
+                  "format": "byte"
+                }
+              }
+            }
+          }
+        }
+      },
+      "$schema": "http://json-schema.org/draft-07/schema",
+      "$id": "http://rekor.sigstore.dev/types/rekord/rekord_v0_0_1_schema.json"
+    },
     "helm": {
       "description": "Helm chart",
       "type": "object",
diff --git a/pkg/pki/minisign/minisign.go b/pkg/pki/minisign/minisign.go
index 2697d96b3f5b2f3cf11f38e5923688daad1ddb3f..2026a33ad45d0402cf39bceb495b7681ea484ca4 100644
--- a/pkg/pki/minisign/minisign.go
+++ b/pkg/pki/minisign/minisign.go
@@ -85,7 +85,7 @@ func (s Signature) CanonicalValue() ([]byte, error) {
 }
 
 // Verify implements the pki.Signature interface
-func (s Signature) Verify(r io.Reader, k interface{}) error {
+func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error {
 	if s.signature == nil {
 		return fmt.Errorf("minisign signature has not been initialized")
 	}
diff --git a/pkg/pki/pgp/pgp.go b/pkg/pki/pgp/pgp.go
index 7e3c7bba01cb01c9e3b98abc058271fa139c3313..80fcd43dc3d62787e72151d7e598025de8677320 100644
--- a/pkg/pki/pgp/pgp.go
+++ b/pkg/pki/pgp/pgp.go
@@ -28,6 +28,8 @@ import (
 	"golang.org/x/crypto/openpgp"
 	"golang.org/x/crypto/openpgp/armor"
 	"golang.org/x/crypto/openpgp/packet"
+
+	sigsig "github.com/sigstore/sigstore/pkg/signature"
 )
 
 // Signature Signature that follows the PGP standard; supports both armored & binary detached signatures
@@ -130,7 +132,7 @@ func (s Signature) CanonicalValue() ([]byte, error) {
 }
 
 // Verify implements the pki.Signature interface
-func (s Signature) Verify(r io.Reader, k interface{}) error {
+func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error {
 	if len(s.signature) == 0 {
 		return fmt.Errorf("PGP signature has not been initialized")
 	}
diff --git a/pkg/pki/pkcs7/pkcs7.go b/pkg/pki/pkcs7/pkcs7.go
index 8e842621bb051d15995c3fae3bd2a74b5461365a..10f29a21e0291c27c31912eeb7f4f426423ffe5f 100644
--- a/pkg/pki/pkcs7/pkcs7.go
+++ b/pkg/pki/pkcs7/pkcs7.go
@@ -29,6 +29,7 @@ import (
 	"strings"
 
 	"github.com/sassoftware/relic/lib/pkcs7"
+	sigsig "github.com/sigstore/sigstore/pkg/signature"
 )
 
 // EmailAddressOID defined by https://oidref.com/1.2.840.113549.1.9.1
@@ -105,7 +106,7 @@ func (s Signature) CanonicalValue() ([]byte, error) {
 }
 
 // Verify implements the pki.Signature interface
-func (s Signature) Verify(r io.Reader, k interface{}) error {
+func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error {
 	if len(*s.raw) == 0 {
 		return fmt.Errorf("PKCS7 signature has not been initialized")
 	}
diff --git a/pkg/pki/pki.go b/pkg/pki/pki.go
index cca198716648e791ee6a4c9aa682ae7174788dde..d1618034d87c85000ec5635751c7ed3232e44be7 100644
--- a/pkg/pki/pki.go
+++ b/pkg/pki/pki.go
@@ -17,6 +17,8 @@ package pki
 
 import (
 	"io"
+
+	sigsig "github.com/sigstore/sigstore/pkg/signature"
 )
 
 // PublicKey Generic object representing a public key (regardless of format & algorithm)
@@ -28,5 +30,5 @@ type PublicKey interface {
 // Signature Generic object representing a signature (regardless of format & algorithm)
 type Signature interface {
 	CanonicalValue() ([]byte, error)
-	Verify(r io.Reader, k interface{}) error
+	Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error
 }
diff --git a/pkg/pki/ssh/ssh.go b/pkg/pki/ssh/ssh.go
index faa4d6d25e3674be3900334fc7c05d00aa638751..8012905aa00c5b5d63bb9769133e5599b6042e42 100644
--- a/pkg/pki/ssh/ssh.go
+++ b/pkg/pki/ssh/ssh.go
@@ -20,6 +20,7 @@ import (
 	"io"
 	"io/ioutil"
 
+	sigsig "github.com/sigstore/sigstore/pkg/signature"
 	"golang.org/x/crypto/ssh"
 )
 
@@ -48,7 +49,7 @@ func (s Signature) CanonicalValue() ([]byte, error) {
 }
 
 // Verify implements the pki.Signature interface
-func (s Signature) Verify(r io.Reader, k interface{}) error {
+func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error {
 	if s.signature == nil {
 		return fmt.Errorf("ssh signature has not been initialized")
 	}
diff --git a/pkg/pki/tuf/tuf.go b/pkg/pki/tuf/tuf.go
index db4cb5c7838571b07e47a751a87234208722407b..1b63049b21769c0289196fc30e291861c377f7f1 100644
--- a/pkg/pki/tuf/tuf.go
+++ b/pkg/pki/tuf/tuf.go
@@ -22,6 +22,7 @@ import (
 	"io/ioutil"
 	"time"
 
+	sigsig "github.com/sigstore/sigstore/pkg/signature"
 	cjson "github.com/tent/canonical-json-go"
 	"github.com/theupdateframework/go-tuf/data"
 	"github.com/theupdateframework/go-tuf/verify"
@@ -80,7 +81,7 @@ func (s Signature) CanonicalValue() ([]byte, error) {
 }
 
 // Verify implements the pki.Signature interface
-func (s Signature) Verify(_ io.Reader, k interface{}) error {
+func (s Signature) Verify(_ io.Reader, k interface{}, opts ...sigsig.VerifyOption) error {
 	key, ok := k.(*PublicKey)
 	if !ok {
 		return fmt.Errorf("invalid public key type for: %v", k)
diff --git a/pkg/pki/x509/x509.go b/pkg/pki/x509/x509.go
index 4933d3a1b49030014f960439f0ebfef55c06aaf0..0513b0a4bfa68c9a65b0284f097886ae6f0b5ad5 100644
--- a/pkg/pki/x509/x509.go
+++ b/pkg/pki/x509/x509.go
@@ -56,7 +56,7 @@ func (s Signature) CanonicalValue() ([]byte, error) {
 }
 
 // Verify implements the pki.Signature interface
-func (s Signature) Verify(r io.Reader, k interface{}) error {
+func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error {
 	if len(s.signature) == 0 {
 		//lint:ignore ST1005 X509 is proper use of term
 		return fmt.Errorf("X509 signature has not been initialized")
@@ -76,7 +76,7 @@ func (s Signature) Verify(r io.Reader, k interface{}) error {
 	if err != nil {
 		return err
 	}
-	return verifier.VerifySignature(bytes.NewReader(s.signature), r)
+	return verifier.VerifySignature(bytes.NewReader(s.signature), r, opts...)
 }
 
 // PublicKey Public Key that follows the x509 standard
diff --git a/pkg/types/hashedrekord/hashedrekord.go b/pkg/types/hashedrekord/hashedrekord.go
new file mode 100644
index 0000000000000000000000000000000000000000..4ae6b52f838c1d26b3f161d412e9410136247382
--- /dev/null
+++ b/pkg/types/hashedrekord/hashedrekord.go
@@ -0,0 +1,75 @@
+//
+// Copyright 2021 The Sigstore 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 hashedrekord
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/pkg/errors"
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/types"
+)
+
+const (
+	KIND = "hashedrekord"
+)
+
+type BaseRekordType struct {
+	types.RekorType
+}
+
+func init() {
+	types.TypeMap.Store(KIND, New)
+}
+
+func New() types.TypeImpl {
+	brt := BaseRekordType{}
+	brt.Kind = KIND
+	brt.VersionMap = VersionMap
+	return &brt
+}
+
+var VersionMap = types.NewSemVerEntryFactoryMap()
+
+func (rt BaseRekordType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
+	if pe == nil {
+		return nil, errors.New("proposed entry cannot be nil")
+	}
+
+	rekord, ok := pe.(*models.Hashedrekord)
+	if !ok {
+		return nil, errors.New(fmt.Sprintf("%s, %s", "cannot unmarshal non-hashed Rekord types", pe.Kind()))
+	}
+
+	return rt.VersionedUnmarshal(rekord, *rekord.APIVersion)
+}
+
+func (rt *BaseRekordType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
+	if version == "" {
+		version = rt.DefaultVersion()
+	}
+	ei, err := rt.VersionedUnmarshal(nil, version)
+	if err != nil {
+		return nil, errors.Wrap(err, "fetching hashed Rekord version implementation")
+	}
+
+	return ei.CreateFromArtifactProperties(ctx, props)
+}
+
+func (rt BaseRekordType) DefaultVersion() string {
+	return "0.0.1"
+}
diff --git a/pkg/types/hashedrekord/hashedrekord_schema.json b/pkg/types/hashedrekord/hashedrekord_schema.json
new file mode 100644
index 0000000000000000000000000000000000000000..e6104f4c873963a494ce240c45721496167b2fc7
--- /dev/null
+++ b/pkg/types/hashedrekord/hashedrekord_schema.json
@@ -0,0 +1,12 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "$id": "http://rekor.sigstore.dev/types/hashedrekord/hasehedrekord_schema.json",
+    "title": "Rekor Schema",
+    "description": "Schema for Rekord objects",
+    "type": "object",
+    "oneOf": [
+        {
+            "$ref": "v0.0.1/hashedrekord_v0_0_1_schema.json"
+        }
+    ]
+}
diff --git a/pkg/types/hashedrekord/hashedrekord_test.go b/pkg/types/hashedrekord/hashedrekord_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d3ed2e28dbe6a451a5254fe395eb57bfda156e35
--- /dev/null
+++ b/pkg/types/hashedrekord/hashedrekord_test.go
@@ -0,0 +1,128 @@
+//
+// Copyright 2021 The Sigstore 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 hashedrekord
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/go-openapi/swag"
+
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/types"
+)
+
+type UnmarshalTester struct {
+	models.Hashedrekord
+}
+
+func (u UnmarshalTester) NewEntry() types.EntryImpl {
+	return &UnmarshalTester{}
+}
+
+func (u UnmarshalTester) Validate() error {
+	return nil
+}
+
+func (u UnmarshalTester) APIVersion() string {
+	return "2.0.1"
+}
+
+func (u UnmarshalTester) IndexKeys() []string {
+	return []string{}
+}
+
+func (u UnmarshalTester) Canonicalize(ctx context.Context) ([]byte, error) {
+	return nil, nil
+}
+
+func (u UnmarshalTester) HasExternalEntities() bool {
+	return false
+}
+
+func (u UnmarshalTester) Unmarshal(pe models.ProposedEntry) error {
+	return nil
+}
+
+func (u UnmarshalTester) Attestation() (string, []byte) {
+	return "", nil
+}
+
+func (u UnmarshalTester) CreateFromArtifactProperties(_ context.Context, _ types.ArtifactProperties) (models.ProposedEntry, error) {
+	return nil, nil
+}
+
+type UnmarshalFailsTester struct {
+	UnmarshalTester
+}
+
+func (u UnmarshalFailsTester) NewEntry() types.EntryImpl {
+	return &UnmarshalFailsTester{}
+}
+
+func (u UnmarshalFailsTester) Unmarshal(pe models.ProposedEntry) error {
+	return errors.New("error")
+}
+
+func TestRekordType(t *testing.T) {
+	// empty to start
+	if VersionMap.Count() != 0 {
+		t.Error("semver range was not blank at start of test")
+	}
+
+	u := UnmarshalTester{}
+	// ensure semver range parser is working
+	invalidSemVerRange := "not a valid semver range"
+	err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry)
+	if err == nil || VersionMap.Count() > 0 {
+		t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap")
+	}
+
+	// valid semver range can be parsed
+	err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry)
+	if err != nil || VersionMap.Count() != 1 {
+		t.Error("valid semver range was not added to SemVerToFacFnMap")
+	}
+
+	u.Hashedrekord.APIVersion = swag.String("2.0.1")
+	brt := New()
+
+	// version requested matches implementation in map
+	if _, err := brt.UnmarshalEntry(&u.Hashedrekord); err != nil {
+		t.Errorf("unexpected error in Unmarshal: %v", err)
+	}
+
+	// version requested fails to match implementation in map
+	u.Hashedrekord.APIVersion = swag.String("1.2.2")
+	if _, err := brt.UnmarshalEntry(&u.Hashedrekord); err == nil {
+		t.Error("unexpected success in Unmarshal for non-matching version")
+	}
+
+	// error in Unmarshal call is raised appropriately
+	u.Hashedrekord.APIVersion = swag.String("2.2.0")
+	u2 := UnmarshalFailsTester{}
+	_ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry)
+	if _, err := brt.UnmarshalEntry(&u.Hashedrekord); err == nil {
+		t.Error("unexpected success in Unmarshal when error is thrown")
+	}
+
+	// version requested fails to match implementation in map
+	u.Hashedrekord.APIVersion = swag.String("not_a_version")
+	if _, err := brt.UnmarshalEntry(&u.Hashedrekord); err == nil {
+		t.Error("unexpected success in Unmarshal for invalid version")
+	}
+}
diff --git a/pkg/types/hashedrekord/v0.0.1/entry.go b/pkg/types/hashedrekord/v0.0.1/entry.go
new file mode 100644
index 0000000000000000000000000000000000000000..740530a31c661542b022f30bdb6ded6eb557be6c
--- /dev/null
+++ b/pkg/types/hashedrekord/v0.0.1/entry.go
@@ -0,0 +1,249 @@
+//
+// Copyright 2021 The Sigstore 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 hashedrekord
+
+import (
+	"bytes"
+	"context"
+	"crypto/sha256"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"path/filepath"
+	"strings"
+
+	"github.com/asaskevich/govalidator"
+	"github.com/go-openapi/strfmt"
+	"github.com/go-openapi/swag"
+	"github.com/pkg/errors"
+
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/log"
+	"github.com/sigstore/rekor/pkg/pki"
+	"github.com/sigstore/rekor/pkg/types"
+	hashedrekord "github.com/sigstore/rekor/pkg/types/hashedrekord"
+	"github.com/sigstore/sigstore/pkg/signature/options"
+)
+
+const (
+	APIVERSION = "0.0.1"
+)
+
+func init() {
+	if err := hashedrekord.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
+		log.Logger.Panic(err)
+	}
+}
+
+type V001Entry struct {
+	HashedRekordObj models.HashedrekordV001Schema
+	keyObj          pki.PublicKey
+	sigObj          pki.Signature
+}
+
+func (v V001Entry) APIVersion() string {
+	return APIVERSION
+}
+
+func NewEntry() types.EntryImpl {
+	return &V001Entry{}
+}
+
+func (v V001Entry) IndexKeys() []string {
+	var result []string
+
+	key, err := v.keyObj.CanonicalValue()
+	if err != nil {
+		log.Logger.Error(err)
+	} else {
+		keyHash := sha256.Sum256(key)
+		result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:])))
+	}
+
+	result = append(result, v.keyObj.EmailAddresses()...)
+
+	if v.HashedRekordObj.Data.Hash != nil {
+		hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.HashedRekordObj.Data.Hash.Algorithm, *v.HashedRekordObj.Data.Hash.Value))
+		result = append(result, hashKey)
+	}
+
+	return result
+}
+
+func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
+	rekord, ok := pe.(*models.Hashedrekord)
+	if !ok {
+		return errors.New("cannot unmarshal non Rekord v0.0.1 type")
+	}
+
+	if err := types.DecodeEntry(rekord.Spec, &v.HashedRekordObj); err != nil {
+		return err
+	}
+
+	// field validation
+	if err := v.HashedRekordObj.Validate(strfmt.Default); err != nil {
+		return err
+	}
+
+	// cross field validation
+	return v.validate()
+}
+
+func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) {
+	if err := v.validate(); err != nil {
+		return nil, types.ValidationError(err)
+	}
+
+	if v.sigObj == nil {
+		return nil, errors.New("signature object not initialized before canonicalization")
+	}
+
+	if v.keyObj == nil {
+		return nil, errors.New("key object not initialized before canonicalization")
+	}
+
+	canonicalEntry := models.HashedrekordV001Schema{}
+
+	// need to canonicalize signature & key content
+	canonicalEntry.Signature = &models.HashedrekordV001SchemaSignature{}
+	var err error
+	canonicalEntry.Signature.Content, err = v.sigObj.CanonicalValue()
+	if err != nil {
+		return nil, err
+	}
+
+	// key URL (if known) is not set deliberately
+	canonicalEntry.Signature.PublicKey = &models.HashedrekordV001SchemaSignaturePublicKey{}
+	canonicalEntry.Signature.PublicKey.Content, err = v.keyObj.CanonicalValue()
+	if err != nil {
+		return nil, err
+	}
+
+	canonicalEntry.Data = &models.HashedrekordV001SchemaData{}
+	canonicalEntry.Data.Hash = v.HashedRekordObj.Data.Hash
+	// data content is not set deliberately
+
+	// wrap in valid object with kind and apiVersion set
+	rekordObj := models.Hashedrekord{}
+	rekordObj.APIVersion = swag.String(APIVERSION)
+	rekordObj.Spec = &canonicalEntry
+
+	return json.Marshal(&rekordObj)
+}
+
+// validate performs cross-field validation for fields in object
+func (v *V001Entry) validate() error {
+	sig := v.HashedRekordObj.Signature
+	if sig == nil {
+		return types.ValidationError(errors.New("missing signature"))
+	}
+	// Hashed rekord type only works for x509 signature types
+	artifactFactory, err := pki.NewArtifactFactory(pki.X509)
+	if err != nil {
+		return types.ValidationError(err)
+	}
+	v.sigObj, err = artifactFactory.NewSignature(bytes.NewReader(sig.Content))
+	if err != nil {
+		return errors.Wrap(err, "creating new signature object")
+	}
+
+	key := sig.PublicKey
+	if key == nil {
+		return types.ValidationError(errors.New("missing public key"))
+	}
+	v.keyObj, err = artifactFactory.NewPublicKey(bytes.NewReader(key.Content))
+	if err != nil {
+		return errors.Wrap(err, "creating new public key object")
+	}
+
+	data := v.HashedRekordObj.Data
+	if data == nil {
+		return types.ValidationError(errors.New("missing data"))
+	}
+
+	hash := data.Hash
+	if hash == nil {
+		return types.ValidationError(errors.New("missing hash"))
+	}
+	if !govalidator.IsHash(swag.StringValue(hash.Value), swag.StringValue(hash.Algorithm)) {
+		return types.ValidationError(errors.New("invalid value for hash"))
+	}
+
+	decoded, err := hex.DecodeString(*hash.Value)
+	if err != nil {
+		return err
+	}
+	if err = v.sigObj.Verify(nil, v.keyObj, options.WithDigest(decoded)); err != nil {
+		return types.ValidationError(errors.Wrap(err, "verifying signature"))
+	}
+
+	return nil
+}
+
+func (v V001Entry) Attestation() (string, []byte) {
+	return "", nil
+}
+
+func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
+	returnVal := models.Hashedrekord{}
+	re := V001Entry{}
+
+	// we will need artifact, public-key, signature
+	re.HashedRekordObj.Data = &models.HashedrekordV001SchemaData{}
+
+	var err error
+
+	re.HashedRekordObj.Signature = &models.HashedrekordV001SchemaSignature{}
+	sigBytes := props.SignatureBytes
+	if sigBytes == nil {
+		if props.SignaturePath == nil {
+			return nil, errors.New("a detached signature must be provided")
+		}
+		sigBytes, err = ioutil.ReadFile(filepath.Clean(props.SignaturePath.Path))
+		if err != nil {
+			return nil, fmt.Errorf("error reading signature file: %w", err)
+		}
+	}
+	re.HashedRekordObj.Signature.Content = strfmt.Base64(sigBytes)
+
+	re.HashedRekordObj.Signature.PublicKey = &models.HashedrekordV001SchemaSignaturePublicKey{}
+	publicKeyBytes := props.PublicKeyBytes
+	if publicKeyBytes == nil {
+		if props.PublicKeyPath == nil {
+			return nil, errors.New("public key must be provided to verify detached signature")
+		}
+		publicKeyBytes, err = ioutil.ReadFile(filepath.Clean(props.PublicKeyPath.Path))
+		if err != nil {
+			return nil, fmt.Errorf("error reading public key file: %w", err)
+		}
+	}
+	re.HashedRekordObj.Signature.PublicKey.Content = strfmt.Base64(publicKeyBytes)
+
+	re.HashedRekordObj.Data.Hash = &models.HashedrekordV001SchemaDataHash{
+		Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256),
+		Value:     swag.String(props.ArtifactHash),
+	}
+
+	if err := re.validate(); err != nil {
+		return nil, err
+	}
+
+	returnVal.APIVersion = swag.String(re.APIVersion())
+	returnVal.Spec = re.HashedRekordObj
+
+	return &returnVal, nil
+}
diff --git a/pkg/types/hashedrekord/v0.0.1/entry_test.go b/pkg/types/hashedrekord/v0.0.1/entry_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7f43851d83153e8b6078fdc5415667cdfb2ab4d6
--- /dev/null
+++ b/pkg/types/hashedrekord/v0.0.1/entry_test.go
@@ -0,0 +1,261 @@
+//
+// Copyright 2021 The Sigstore 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 hashedrekord
+
+import (
+	"bytes"
+	"context"
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/hex"
+	"encoding/pem"
+	"reflect"
+	"testing"
+
+	"github.com/go-openapi/runtime"
+	"github.com/go-openapi/swag"
+	"go.uber.org/goleak"
+
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/types"
+	"github.com/sigstore/sigstore/pkg/signature"
+)
+
+func TestMain(m *testing.M) {
+	goleak.VerifyTestMain(m)
+}
+
+func TestNewEntryReturnType(t *testing.T) {
+	entry := NewEntry()
+	if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() {
+		t.Errorf("invalid type returned from NewEntry: %T", entry)
+	}
+}
+
+func TestCrossFieldValidation(t *testing.T) {
+	type TestCase struct {
+		caseDesc                  string
+		entry                     V001Entry
+		expectUnmarshalSuccess    bool
+		expectCanonicalizeSuccess bool
+	}
+
+	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		t.Fatal(err)
+	}
+	der, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
+	if err != nil {
+		t.Fatal(err)
+	}
+	keyBytes := pem.EncodeToMemory(&pem.Block{
+		Bytes: der,
+		Type:  "PUBLIC KEY",
+	})
+
+	dataBytes := []byte("sign me!")
+	h := sha256.Sum256(dataBytes)
+	dataSHA := hex.EncodeToString(h[:])
+
+	signer, _ := signature.LoadSigner(key, crypto.SHA256)
+	sigBytes, _ := signer.SignMessage(bytes.NewReader(dataBytes))
+
+	incorrectLengthHash := sha256.Sum224(dataBytes)
+	incorrectLengthSHA := hex.EncodeToString(incorrectLengthHash[:])
+
+	badHash := sha256.Sum256(keyBytes)
+	badDataSHA := hex.EncodeToString(badHash[:])
+
+	testCases := []TestCase{
+		{
+			caseDesc:               "empty obj",
+			entry:                  V001Entry{},
+			expectUnmarshalSuccess: false,
+		},
+		{
+			caseDesc: "signature without url or content",
+			entry: V001Entry{
+				HashedRekordObj: models.HashedrekordV001Schema{
+					Signature: &models.HashedrekordV001SchemaSignature{},
+				},
+			},
+			expectUnmarshalSuccess: false,
+		},
+		{
+			caseDesc: "signature without public key",
+			entry: V001Entry{
+				HashedRekordObj: models.HashedrekordV001Schema{
+					Signature: &models.HashedrekordV001SchemaSignature{
+						Content: sigBytes,
+					},
+				},
+			},
+			expectUnmarshalSuccess: false,
+		},
+		{
+			caseDesc: "signature with empty public key",
+			entry: V001Entry{
+				HashedRekordObj: models.HashedrekordV001Schema{
+					Signature: &models.HashedrekordV001SchemaSignature{
+						Content:   sigBytes,
+						PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{},
+					},
+				},
+			},
+			expectUnmarshalSuccess: false,
+		},
+		{
+			caseDesc: "signature without data",
+			entry: V001Entry{
+				HashedRekordObj: models.HashedrekordV001Schema{
+					Signature: &models.HashedrekordV001SchemaSignature{
+						Content: sigBytes,
+						PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{
+							Content: keyBytes,
+						},
+					},
+				},
+			},
+			expectUnmarshalSuccess: false,
+		},
+		{
+			caseDesc: "signature with empty data",
+			entry: V001Entry{
+				HashedRekordObj: models.HashedrekordV001Schema{
+					Signature: &models.HashedrekordV001SchemaSignature{
+						Content: sigBytes,
+						PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{
+							Content: keyBytes,
+						},
+					},
+					Data: &models.HashedrekordV001SchemaData{},
+				},
+			},
+			expectUnmarshalSuccess: false,
+		},
+		{
+			caseDesc: "signature with hash",
+			entry: V001Entry{
+				HashedRekordObj: models.HashedrekordV001Schema{
+					Signature: &models.HashedrekordV001SchemaSignature{
+						Content: sigBytes,
+						PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{
+							Content: keyBytes,
+						},
+					},
+					Data: &models.HashedrekordV001SchemaData{
+						Hash: &models.HashedrekordV001SchemaDataHash{
+							Value:     swag.String(dataSHA),
+							Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256),
+						},
+					},
+				},
+			},
+			expectUnmarshalSuccess:    true,
+			expectCanonicalizeSuccess: true,
+		},
+		{
+			caseDesc: "signature with invalid sha length",
+			entry: V001Entry{
+				HashedRekordObj: models.HashedrekordV001Schema{
+					Signature: &models.HashedrekordV001SchemaSignature{
+						Content: sigBytes,
+						PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{
+							Content: keyBytes,
+						},
+					},
+					Data: &models.HashedrekordV001SchemaData{
+						Hash: &models.HashedrekordV001SchemaDataHash{
+							Value:     swag.String(incorrectLengthSHA),
+							Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256),
+						},
+					},
+				},
+			},
+			expectUnmarshalSuccess:    false,
+			expectCanonicalizeSuccess: false,
+		},
+		{
+			caseDesc: "signature with hash & invalid signature",
+			entry: V001Entry{
+				HashedRekordObj: models.HashedrekordV001Schema{
+					Signature: &models.HashedrekordV001SchemaSignature{
+						Content: sigBytes,
+						PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{
+							Content: keyBytes,
+						},
+					},
+					Data: &models.HashedrekordV001SchemaData{
+						Hash: &models.HashedrekordV001SchemaDataHash{
+							Value:     swag.String(badDataSHA),
+							Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256),
+						},
+					},
+				},
+			},
+			expectUnmarshalSuccess:    false,
+			expectCanonicalizeSuccess: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		if err := tc.entry.validate(); (err == nil) != tc.expectUnmarshalSuccess {
+			t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err)
+		}
+
+		v := &V001Entry{}
+		r := models.Hashedrekord{
+			APIVersion: swag.String(tc.entry.APIVersion()),
+			Spec:       tc.entry.HashedRekordObj,
+		}
+
+		unmarshalAndValidate := func() error {
+			if err := v.Unmarshal(&r); err != nil {
+				return err
+			}
+			if err := v.validate(); err != nil {
+				return err
+			}
+			return nil
+		}
+
+		if err := unmarshalAndValidate(); (err == nil) != tc.expectUnmarshalSuccess {
+			t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err)
+		}
+
+		b, err := v.Canonicalize(context.TODO())
+		if (err == nil) != tc.expectCanonicalizeSuccess {
+			t.Errorf("unexpected result from Canonicalize for '%v': %v", tc.caseDesc, err)
+		} else if err != nil {
+			if _, ok := err.(types.ValidationError); !ok {
+				t.Errorf("canonicalize returned an unexpected error that isn't of type types.ValidationError: %v", err)
+			}
+		}
+		if b != nil {
+			pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer())
+			if err != nil {
+				t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err)
+			}
+			if _, err := types.NewEntry(pe); err != nil {
+				t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err)
+			}
+		}
+	}
+}
diff --git a/pkg/types/hashedrekord/v0.0.1/hashedrekord_v0_0_1_schema.json b/pkg/types/hashedrekord/v0.0.1/hashedrekord_v0_0_1_schema.json
new file mode 100644
index 0000000000000000000000000000000000000000..b1ef65052b2d45ba0d6e69c92529384c30975952
--- /dev/null
+++ b/pkg/types/hashedrekord/v0.0.1/hashedrekord_v0_0_1_schema.json
@@ -0,0 +1,54 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "$id": "http://rekor.sigstore.dev/types/rekord/rekord_v0_0_1_schema.json",
+    "title": "Hashed Rekor v0.0.1 Schema",
+    "description": "Schema for Hashed Rekord object",
+    "type": "object",
+    "properties": {
+        "signature": {
+            "description": "Information about the detached signature associated with the entry",
+            "type": "object",
+            "properties": {
+                "content": {
+                    "description": "Specifies the content of the signature inline within the document",
+                    "type": "string",
+                    "format": "byte"
+                },
+                "publicKey" : {
+                    "description": "The public key that can verify the signature",
+                    "type": "object",
+                    "properties": {
+                        "content": {
+                            "description": "Specifies the content of the public key inline within the document",
+                            "type": "string",
+                            "format": "byte"
+                        }
+                    }
+                }
+            }
+        },
+        "data": {
+            "description": "Information about the content associated with the entry",
+            "type": "object",
+            "properties": {
+                "hash": {
+                    "description": "Specifies the hash algorithm and value for the content",
+                    "type": "object",
+                    "properties": {
+                        "algorithm": {
+                            "description": "The hashing function used to compute the hash value",
+                            "type": "string",
+                            "enum": [ "sha256" ]
+                        },
+                        "value": {
+                            "description": "The hash value for the content",
+                            "type": "string"
+                        }
+                    },
+                    "required": [ "algorithm", "value" ]
+                }
+            }        
+        }
+    },
+    "required": [ "signature", "data" ]
+}
diff --git a/tests/e2e_test.go b/tests/e2e_test.go
index 4a2344bda4384ca6f2bee4dd22e909a84be07ebc..94790b42b3797ff36aa1ac5f0ac376172299843e 100644
--- a/tests/e2e_test.go
+++ b/tests/e2e_test.go
@@ -136,6 +136,35 @@ func TestUploadVerifyRekord(t *testing.T) {
 	outputContains(t, out, "Inclusion Proof:")
 }
 
+func TestUploadVerifyHashedRekord(t *testing.T) {
+
+	// Create a random artifact and sign it.
+	artifactPath := filepath.Join(t.TempDir(), "artifact")
+	sigPath := filepath.Join(t.TempDir(), "signature.asc")
+
+	createdX509SignedArtifact(t, artifactPath, sigPath)
+	dataBytes, _ := ioutil.ReadFile(artifactPath)
+	h := sha256.Sum256(dataBytes)
+	dataSHA := hex.EncodeToString(h[:])
+
+	// Write the public key to a file
+	pubPath := filepath.Join(t.TempDir(), "pubKey.asc")
+	if err := ioutil.WriteFile(pubPath, []byte(rsaCert), 0644); err != nil {
+		t.Fatal(err)
+	}
+
+	// Verify should fail initially
+	runCliErr(t, "verify", "--type=hashedrekord", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath)
+
+	// It should upload successfully.
+	out := runCli(t, "upload", "--type=hashedrekord", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath)
+	outputContains(t, out, "Created entry at")
+
+	// Now we should be able to verify it.
+	out = runCli(t, "verify", "--type=hashedrekord", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath)
+	outputContains(t, out, "Inclusion Proof:")
+}
+
 func TestUploadVerifyRpm(t *testing.T) {
 
 	// Create a random rpm and sign it.
diff --git a/types.md b/types.md
index 400e2304088930a5f2716bbea2d47249bba300dc..a7b14222292cdd60adec51ee7cf70fbb62c37fc0 100644
--- a/types.md
+++ b/types.md
@@ -287,3 +287,57 @@ Body: {
 }
 
 ```
+
+
+## Hashed rekord
+
+This is similar to a rekord type, but allows hashed data instead of supplying the full content that was signed. This is suitable for uploading signatures on large payloads. This is only compatible with x509 / PKIX signature types.
+
+Generate a keypair with:
+
+```console
+$ openssl ecparam -genkey -name prime256v1 > ec_private.pem
+$ openssl ec -in ec_private.pem -pubout > ec_public.pem
+read EC key
+writing EC key
+```
+
+Sign the file with:
+
+```console
+$ openssl dgst -sha256 -sign ec_private.pem -out README.md.sig README.md
+```
+
+Upload it to rekor with:
+
+```console
+$ ./rekor-cli upload --type hashedrekord:0.0.1 --artifact-hash $(sha256sum  README.md | awk '{print $1}') --signature README.md.sig --pki-format=x509 --public-key=ec_public.pem
+Created entry at index 12, available at: https://rekor.sigstore.dev/api/v1/log/entries/31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d
+```
+
+View the entry with:
+
+```console
+$ rekor-cli get --uuid=31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d
+LogID: b3e217db795022552080ed8b22596131c63f3aa198e83450f3dba9e686633641
+Index: 12
+IntegratedTime: 2021-11-17T21:59:49Z
+UUID: 31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d
+Body: {
+  "HashedRekordObj": {
+    "data": {
+      "hash": {
+        "algorithm": "sha256",
+        "value": "9249e5dfa2ede1c5bd89c49bf9beaf3e9afda2d961dea7cda7f639210179cd16"
+      }
+    },
+    "signature": {
+      "content": "MEQCIG9s7GVWH67OkeXPQvM/XAcLW7N0xiFZWez95uR+GlXyAiBW+DPRaYvgtpQglQLtqujwb+xQBd8I70Vk/2vDB+G3uQ==",
+      "publicKey": {
+        "content": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFR0JPTVdDanViVTVldkJ0OGcxWTZTR1ZoZ29OVwpjY2lrbHlpTEJQajQ5Um40WVFhTjRJS0xySi9nQlROU2tOREdQbHFvNHVjTVg3L21PZmlBNkVHS09BPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
+      }
+    }
+  }
+}
+
+```
\ No newline at end of file