Repository: sigstore/rekor
Pull request #1487 (2023-05-12 23:19:55)

add dsse type
#### Summary
This adds a new pluggable type `dsse`, which adds entries for [DSSE](https://github.com/secure-systems-lab/dsse) JSON documents which are often used with in-toto attestations. There are a few significant differences between this `dsse` type and the already existing `intoto` types:

- The JSON schema more clearly separates the proposed entry from what is persisted to simplify client expectations
- The DSSE envelope is provided as a JSON string, rather than double-encoding values in base64
- The DSSE envelope is not stored by Rekor; this is to set the client expectation that attestation storage in Rekor should not be relied upon for attestation discovery. 

#### Release Note

* Adds a new entry type `dsse` which persisted information about DSSE envelopes without storing the envelope in Rekor

#### Documentation

- [ ] Add docs on type to docs.sigstore.dev



codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sigstore/rekor/pull/1487?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore) Report
> Merging [#1487](https://app.codecov.io/gh/sigstore/rekor/pull/1487?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore) (e0f9be1) into [main](https://app.codecov.io/gh/sigstore/rekor/commit/3282e1ab3e9b8f773e8a44861a31a3df9adf01ce?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore) (3282e1a) will **decrease** coverage by `18.92%`.
> The diff coverage is `63.72%`.

> :exclamation: Current head e0f9be1 differs from pull request most recent head a85cde9. Consider uploading reports for the commit a85cde9 to get more accurate results

```diff
@@             Coverage Diff             @@
##             main    #1487       +/-   ##
===========================================
- Coverage   64.33%   45.42%   -18.92%     
===========================================
  Files          80       73        -7     
  Lines        7789     7360      -429     
===========================================
- Hits         5011     3343     -1668     
- Misses       2129     3664     +1535     
+ Partials      649      353      -296     
```

| Flag | Coverage Δ | |
|---|---|---|
| e2etests | `?` | |
| unittests | `45.42% <63.72%> (+0.76%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://app.codecov.io/gh/sigstore/rekor/pull/1487?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore) | Coverage Δ | |
|---|---|---|
| [cmd/rekor-cli/app/root.go](https://app.codecov.io/gh/sigstore/rekor/pull/1487?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore#diff-Y21kL3Jla29yLWNsaS9hcHAvcm9vdC5nbw==) | `19.29% <ø> (-47.37%)` | :arrow_down: |
| [pkg/types/dsse/v0.0.1/entry.go](https://app.codecov.io/gh/sigstore/rekor/pull/1487?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore#diff-cGtnL3R5cGVzL2Rzc2UvdjAuMC4xL2VudHJ5Lmdv) | `60.07% <60.07%> (ø)` | |
| [pkg/types/dsse/dsse.go](https://app.codecov.io/gh/sigstore/rekor/pull/1487?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore#diff-cGtnL3R5cGVzL2Rzc2UvZHNzZS5nbw==) | `100.00% <100.00%> (ø)` | |

... and [54 files with indirect coverage changes](https://app.codecov.io/gh/sigstore/rekor/pull/1487/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sigstore)



Patch:
diff --git a/Dockerfile b/Dockerfile
index 2514d03..2f8beb4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM golang:1.20.4@sha256:c6447e16fb7bada7698fb395ab3c7df19896f1824fe72165441b3756d3576856 AS builder
+FROM golang:1.20.4@sha256:690e4135bf2a4571a572bfd5ddfa806b1cb9c3dea0446ebadaf32bc2ea09d4f9 AS builder
 ENV APP_ROOT=/opt/app-root
 ENV GOPATH=$APP_ROOT
 
@@ -31,7 +31,7 @@ RUN CGO_ENABLED=0 go build -gcflags "all=-N -l" -ldflags "${SERVER_LDFLAGS}" -o
 RUN go test -c -ldflags "${SERVER_LDFLAGS}" -cover -covermode=count -coverpkg=./... -o rekor-server_test ./cmd/rekor-server
 
 # Multi-Stage production build
-FROM golang:1.20.4@sha256:c6447e16fb7bada7698fb395ab3c7df19896f1824fe72165441b3756d3576856 as deploy
+FROM golang:1.20.4@sha256:690e4135bf2a4571a572bfd5ddfa806b1cb9c3dea0446ebadaf32bc2ea09d4f9 as deploy
 
 # Retrieve the binary from the previous stage
 COPY --from=builder /opt/app-root/src/rekor-server /usr/local/bin/rekor-server
diff --git a/Makefile b/Makefile
index 46f1268..9c2467b 100644
--- a/Makefile
+++ b/Makefile
@@ -65,8 +65,8 @@ SERVER_LDFLAGS=$(REKOR_LDFLAGS)
 
 Makefile.swagger: $(SWAGGER) $(OPENAPIDEPS)
 	$(SWAGGER) validate openapi.yaml
-	$(SWAGGER) generate client -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --additional-initialism=TUF
-	$(SWAGGER) generate server -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --exclude-main -A rekor_server --flag-strategy=pflag --default-produces application/json --additional-initialism=TUF
+	$(SWAGGER) generate client -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --additional-initialism=TUF --additional-initialism=DSSE
+	$(SWAGGER) generate server -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --exclude-main -A rekor_server --flag-strategy=pflag --default-produces application/json --additional-initialism=TUF --additional-initialism=DSSE
 	@echo "# This file is generated after swagger runs as part of the build; do not edit!" > Makefile.swagger
 	@echo "SWAGGER_GEN=`find pkg/generated/client pkg/generated/models pkg/generated/restapi -iname '*.go' | grep -v 'configure_rekor_server' | sort -d | tr '\n' ' ' | sed 's/ $$//'`" >> Makefile.swagger;
 
diff --git a/Makefile.swagger b/Makefile.swagger
index 54f2113..8d03c30 100644
--- a/Makefile.swagger
+++ b/Makefile.swagger
@@ -1,2 +1,2 @@
 # This file is generated after swagger runs as part of the build; do not edit!
-SWAGGER_GEN=pkg/generated/client/entries/create_log_entry_parameters.go pkg/generated/client/entries/create_log_entry_responses.go pkg/generated/client/entries/entries_client.go pkg/generated/client/entries/get_log_entry_by_index_parameters.go pkg/generated/client/entries/get_log_entry_by_index_responses.go pkg/generated/client/entries/get_log_entry_by_uuid_parameters.go pkg/generated/client/entries/get_log_entry_by_uuid_responses.go pkg/generated/client/entries/search_log_query_parameters.go pkg/generated/client/entries/search_log_query_responses.go pkg/generated/client/index/index_client.go pkg/generated/client/index/search_index_parameters.go pkg/generated/client/index/search_index_responses.go pkg/generated/client/pubkey/get_public_key_parameters.go pkg/generated/client/pubkey/get_public_key_responses.go pkg/generated/client/pubkey/pubkey_client.go pkg/generated/client/rekor_client.go pkg/generated/client/tlog/get_log_info_parameters.go pkg/generated/client/tlog/get_log_info_responses.go pkg/generated/client/tlog/get_log_proof_parameters.go pkg/generated/client/tlog/get_log_proof_responses.go pkg/generated/client/tlog/tlog_client.go pkg/generated/models/alpine.go pkg/generated/models/alpine_schema.go pkg/generated/models/alpine_v001_schema.go pkg/generated/models/consistency_proof.go pkg/generated/models/cose.go pkg/generated/models/cose_schema.go pkg/generated/models/cose_v001_schema.go pkg/generated/models/error.go pkg/generated/models/hashedrekord.go pkg/generated/models/hashedrekord_schema.go pkg/generated/models/hashedrekord_v001_schema.go pkg/generated/models/helm.go pkg/generated/models/helm_schema.go pkg/generated/models/helm_v001_schema.go pkg/generated/models/inactive_shard_log_info.go pkg/generated/models/inclusion_proof.go pkg/generated/models/intoto.go pkg/generated/models/intoto_schema.go pkg/generated/models/intoto_v001_schema.go pkg/generated/models/intoto_v002_schema.go pkg/generated/models/jar.go pkg/generated/models/jar_schema.go pkg/generated/models/jar_v001_schema.go pkg/generated/models/log_entry.go pkg/generated/models/log_info.go pkg/generated/models/proposed_entry.go pkg/generated/models/rekord.go pkg/generated/models/rekord_schema.go pkg/generated/models/rekord_v001_schema.go pkg/generated/models/rfc3161.go pkg/generated/models/rfc3161_schema.go pkg/generated/models/rfc3161_v001_schema.go pkg/generated/models/rpm.go pkg/generated/models/rpm_schema.go pkg/generated/models/rpm_v001_schema.go pkg/generated/models/search_index.go pkg/generated/models/search_log_query.go pkg/generated/models/tuf.go pkg/generated/models/tuf_schema.go pkg/generated/models/tuf_v001_schema.go pkg/generated/restapi/doc.go pkg/generated/restapi/embedded_spec.go pkg/generated/restapi/operations/entries/create_log_entry.go pkg/generated/restapi/operations/entries/create_log_entry_parameters.go pkg/generated/restapi/operations/entries/create_log_entry_responses.go pkg/generated/restapi/operations/entries/create_log_entry_urlbuilder.go pkg/generated/restapi/operations/entries/get_log_entry_by_index.go pkg/generated/restapi/operations/entries/get_log_entry_by_index_parameters.go pkg/generated/restapi/operations/entries/get_log_entry_by_index_responses.go pkg/generated/restapi/operations/entries/get_log_entry_by_index_urlbuilder.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_parameters.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_responses.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_urlbuilder.go pkg/generated/restapi/operations/entries/search_log_query.go pkg/generated/restapi/operations/entries/search_log_query_parameters.go pkg/generated/restapi/operations/entries/search_log_query_responses.go pkg/generated/restapi/operations/entries/search_log_query_urlbuilder.go pkg/generated/restapi/operations/index/search_index.go pkg/generated/restapi/operations/index/search_index_parameters.go pkg/generated/restapi/operations/index/search_index_responses.go pkg/generated/restapi/operations/index/search_index_urlbuilder.go pkg/generated/restapi/operations/pubkey/get_public_key.go pkg/generated/restapi/operations/pubkey/get_public_key_parameters.go pkg/generated/restapi/operations/pubkey/get_public_key_responses.go pkg/generated/restapi/operations/pubkey/get_public_key_urlbuilder.go pkg/generated/restapi/operations/rekor_server_api.go pkg/generated/restapi/operations/tlog/get_log_info.go pkg/generated/restapi/operations/tlog/get_log_info_parameters.go pkg/generated/restapi/operations/tlog/get_log_info_responses.go pkg/generated/restapi/operations/tlog/get_log_info_urlbuilder.go pkg/generated/restapi/operations/tlog/get_log_proof.go pkg/generated/restapi/operations/tlog/get_log_proof_parameters.go pkg/generated/restapi/operations/tlog/get_log_proof_responses.go pkg/generated/restapi/operations/tlog/get_log_proof_urlbuilder.go pkg/generated/restapi/server.go
+SWAGGER_GEN=pkg/generated/client/entries/create_log_entry_parameters.go pkg/generated/client/entries/create_log_entry_responses.go pkg/generated/client/entries/entries_client.go pkg/generated/client/entries/get_log_entry_by_index_parameters.go pkg/generated/client/entries/get_log_entry_by_index_responses.go pkg/generated/client/entries/get_log_entry_by_uuid_parameters.go pkg/generated/client/entries/get_log_entry_by_uuid_responses.go pkg/generated/client/entries/search_log_query_parameters.go pkg/generated/client/entries/search_log_query_responses.go pkg/generated/client/index/index_client.go pkg/generated/client/index/search_index_parameters.go pkg/generated/client/index/search_index_responses.go pkg/generated/client/pubkey/get_public_key_parameters.go pkg/generated/client/pubkey/get_public_key_responses.go pkg/generated/client/pubkey/pubkey_client.go pkg/generated/client/rekor_client.go pkg/generated/client/tlog/get_log_info_parameters.go pkg/generated/client/tlog/get_log_info_responses.go pkg/generated/client/tlog/get_log_proof_parameters.go pkg/generated/client/tlog/get_log_proof_responses.go pkg/generated/client/tlog/tlog_client.go pkg/generated/models/alpine.go pkg/generated/models/alpine_schema.go pkg/generated/models/alpine_v001_schema.go pkg/generated/models/consistency_proof.go pkg/generated/models/cose.go pkg/generated/models/cose_schema.go pkg/generated/models/cose_v001_schema.go pkg/generated/models/dsse.go pkg/generated/models/dsse_schema.go pkg/generated/models/dsse_v001_schema.go pkg/generated/models/error.go pkg/generated/models/hashedrekord.go pkg/generated/models/hashedrekord_schema.go pkg/generated/models/hashedrekord_v001_schema.go pkg/generated/models/helm.go pkg/generated/models/helm_schema.go pkg/generated/models/helm_v001_schema.go pkg/generated/models/inactive_shard_log_info.go pkg/generated/models/inclusion_proof.go pkg/generated/models/intoto.go pkg/generated/models/intoto_schema.go pkg/generated/models/intoto_v001_schema.go pkg/generated/models/intoto_v002_schema.go pkg/generated/models/jar.go pkg/generated/models/jar_schema.go pkg/generated/models/jar_v001_schema.go pkg/generated/models/log_entry.go pkg/generated/models/log_info.go pkg/generated/models/proposed_entry.go pkg/generated/models/rekord.go pkg/generated/models/rekord_schema.go pkg/generated/models/rekord_v001_schema.go pkg/generated/models/rfc3161.go pkg/generated/models/rfc3161_schema.go pkg/generated/models/rfc3161_v001_schema.go pkg/generated/models/rpm.go pkg/generated/models/rpm_schema.go pkg/generated/models/rpm_v001_schema.go pkg/generated/models/search_index.go pkg/generated/models/search_log_query.go pkg/generated/models/tuf.go pkg/generated/models/tuf_schema.go pkg/generated/models/tuf_v001_schema.go pkg/generated/restapi/doc.go pkg/generated/restapi/embedded_spec.go pkg/generated/restapi/operations/entries/create_log_entry.go pkg/generated/restapi/operations/entries/create_log_entry_parameters.go pkg/generated/restapi/operations/entries/create_log_entry_responses.go pkg/generated/restapi/operations/entries/create_log_entry_urlbuilder.go pkg/generated/restapi/operations/entries/get_log_entry_by_index.go pkg/generated/restapi/operations/entries/get_log_entry_by_index_parameters.go pkg/generated/restapi/operations/entries/get_log_entry_by_index_responses.go pkg/generated/restapi/operations/entries/get_log_entry_by_index_urlbuilder.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_parameters.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_responses.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_urlbuilder.go pkg/generated/restapi/operations/entries/search_log_query.go pkg/generated/restapi/operations/entries/search_log_query_parameters.go pkg/generated/restapi/operations/entries/search_log_query_responses.go pkg/generated/restapi/operations/entries/search_log_query_urlbuilder.go pkg/generated/restapi/operations/index/search_index.go pkg/generated/restapi/operations/index/search_index_parameters.go pkg/generated/restapi/operations/index/search_index_responses.go pkg/generated/restapi/operations/index/search_index_urlbuilder.go pkg/generated/restapi/operations/pubkey/get_public_key.go pkg/generated/restapi/operations/pubkey/get_public_key_parameters.go pkg/generated/restapi/operations/pubkey/get_public_key_responses.go pkg/generated/restapi/operations/pubkey/get_public_key_urlbuilder.go pkg/generated/restapi/operations/rekor_server_api.go pkg/generated/restapi/operations/tlog/get_log_info.go pkg/generated/restapi/operations/tlog/get_log_info_parameters.go pkg/generated/restapi/operations/tlog/get_log_info_responses.go pkg/generated/restapi/operations/tlog/get_log_info_urlbuilder.go pkg/generated/restapi/operations/tlog/get_log_proof.go pkg/generated/restapi/operations/tlog/get_log_proof_parameters.go pkg/generated/restapi/operations/tlog/get_log_proof_responses.go pkg/generated/restapi/operations/tlog/get_log_proof_urlbuilder.go pkg/generated/restapi/server.go
diff --git a/cmd/backfill-redis/main.go b/cmd/backfill-redis/main.go
index 5c0df33..7334da9 100644
--- a/cmd/backfill-redis/main.go
+++ b/cmd/backfill-redis/main.go
@@ -47,12 +47,11 @@ 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/cose/v0.0.1"
+	_ "github.com/sigstore/rekor/pkg/types/dsse/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"
-
-	// remove 0.0.2 intoto type due to bugs
-	// _ "github.com/sigstore/rekor/pkg/types/intoto/v0.0.2"
+	_ "github.com/sigstore/rekor/pkg/types/intoto/v0.0.2"
 	_ "github.com/sigstore/rekor/pkg/types/jar/v0.0.1"
 	_ "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1"
 	_ "github.com/sigstore/rekor/pkg/types/rfc3161/v0.0.1"
diff --git a/cmd/rekor-cli/app/root.go b/cmd/rekor-cli/app/root.go
index f4db874..4dc81b7 100644
--- a/cmd/rekor-cli/app/root.go
+++ b/cmd/rekor-cli/app/root.go
@@ -30,6 +30,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/cose/v0.0.1"
+	_ "github.com/sigstore/rekor/pkg/types/dsse/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"
diff --git a/cmd/rekor-server/app/serve.go b/cmd/rekor-server/app/serve.go
index b7628ae..5182955 100644
--- a/cmd/rekor-server/app/serve.go
+++ b/cmd/rekor-server/app/serve.go
@@ -34,6 +34,8 @@ import (
 	alpine_v001 "github.com/sigstore/rekor/pkg/types/alpine/v0.0.1"
 	"github.com/sigstore/rekor/pkg/types/cose"
 	cose_v001 "github.com/sigstore/rekor/pkg/types/cose/v0.0.1"
+	"github.com/sigstore/rekor/pkg/types/dsse"
+	dsse_v001 "github.com/sigstore/rekor/pkg/types/dsse/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"
@@ -97,6 +99,7 @@ var serveCmd = &cobra.Command{
 			helm.KIND:         {helm_v001.APIVERSION},
 			tuf.KIND:          {tuf_v001.APIVERSION},
 			hashedrekord.KIND: {hashedrekord_v001.APIVERSION},
+			dsse.KIND:         {dsse_v001.APIVERSION},
 		}
 
 		for k, v := range pluggableTypeMap {
diff --git a/go.mod b/go.mod
index aadc344..0629de6 100644
--- a/go.mod
+++ b/go.mod
@@ -64,16 +64,53 @@ require (
 require (
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
 	filippo.io/edwards25519 v1.0.0 // indirect
+	github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
+	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
+	github.com/Azure/go-autorest/autorest v0.11.29 // indirect
+	github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect
+	github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
+	github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
+	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
+	github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
+	github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
+	github.com/Azure/go-autorest/logger v0.2.1 // indirect
+	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
+	github.com/aws/aws-sdk-go-v2 v1.18.0 // indirect
+	github.com/aws/aws-sdk-go-v2/config v1.18.23 // indirect
+	github.com/aws/aws-sdk-go-v2/credentials v1.13.22 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect
+	github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.18.11 // indirect
+	github.com/aws/smithy-go v1.13.5 // indirect
+	github.com/cenkalti/backoff/v3 v3.2.2 // indirect
 	github.com/cyphar/filepath-securejoin v0.2.3 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/dimchansky/utfbom v1.1.1 // indirect
 	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+	github.com/go-jose/go-jose/v3 v3.0.0 // indirect
 	github.com/go-logr/logr v1.2.3 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
 	github.com/google/s2a-go v0.1.3 // indirect
+	github.com/hashicorp/errwrap v1.1.0 // indirect
+	github.com/hashicorp/go-multierror v1.1.1 // indirect
+	github.com/hashicorp/go-rootcerts v1.0.2 // indirect
+	github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
+	github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
+	github.com/hashicorp/go-sockaddr v1.0.2 // indirect
+	github.com/hashicorp/vault/api v1.9.1 // indirect
 	github.com/jellydator/ttlcache/v3 v3.0.1 // indirect
+	github.com/ryanuber/go-glob v1.0.0 // indirect
 	go.opentelemetry.io/otel v1.14.0 // indirect
 	go.opentelemetry.io/otel/trace v1.14.0 // indirect
+	golang.org/x/time v0.3.0 // indirect
 	k8s.io/klog/v2 v2.100.1 // indirect
 )
 
diff --git a/go.sum b/go.sum
index 77fdd29..33eed9d 100644
--- a/go.sum
+++ b/go.sum
@@ -430,6 +430,7 @@ github.com/Azure/azure-sdk-for-go v63.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
 github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go v66.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
+github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.2/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
@@ -456,9 +457,11 @@ github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK
 github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
 github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
+github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
 github.com/Azure/go-autorest/autorest v0.11.25/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
 github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
 github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
+github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs=
 github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
 github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
 github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
@@ -466,11 +469,15 @@ github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3
 github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc=
 github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=
 github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
+github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
 github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc=
+github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0=
 github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
 github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
 github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
 github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
 github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
 github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
 github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
@@ -563,22 +570,29 @@ github.com/aws/aws-sdk-go v1.44.259 h1:7yDn1dcv4DZFMKpu+2exIH5O6ipNj9qXrKfdMUaIJ
 github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
 github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
 github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY=
+github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
 github.com/aws/aws-sdk-go-v2/config v1.18.12/go.mod h1:J36fOhj1LQBr+O4hJCiT8FwVvieeoSGOtPuvhKlsNu8=
 github.com/aws/aws-sdk-go-v2/config v1.18.23 h1:gc3lPsAnZpwfi2exupmgHfva0JiAY2BWDg5JWYlmA28=
+github.com/aws/aws-sdk-go-v2/config v1.18.23/go.mod h1:rx0ruaQ+gk3OrLFHRRx56lA//XxP8K8uPzeNiKNuWVY=
 github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtbM2J48ra2+Ltu+tmwr/jO0KA=
 github.com/aws/aws-sdk-go-v2/credentials v1.13.22 h1:Hp9rwJS4giQ48xqonRV/s7QcDf/wxF6UY7osRmBabvI=
+github.com/aws/aws-sdk-go-v2/credentials v1.13.22/go.mod h1:BfNcm6A9nSd+bzejDcMJ5RE+k6WbkCwWkQil7q4heRk=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM=
 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.51 h1:iTFYCAdKzSAjGnVIUe88Hxvix0uaBqr0Rv7qJEOX5hE=
 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.51/go.mod h1:7Grl2gV+dx9SWrUIgwwlUvU40t7+lOSbx34XwfmsTkY=
 github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY=
 github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk=
 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc=
 github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19 h1:FGvpyTg2LKEmMrLlpjOgkoNp9XF5CGeyAyo33LdqZW8=
 github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19/go.mod h1:8W88sW3PjamQpKFUQvHWWKay6ARsNvZnzU7+a4apubw=
 github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
@@ -588,10 +602,12 @@ github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23 h1:c5+bNdV8E4fIPt
 github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23/go.mod h1:1jcUfF+FAOEwtIcNiHPaV4TSoZqkUIPzrohmD7fb95c=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
 github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22 h1:ISLJ2BKXe4zzyZ7mp5ewKECiw0U7KpLgS3S6OxY9Cm0=
 github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22/go.mod h1:QFVbqK54XArazLvn2wvWMRBi/jGrWii46qbr5DyPGjc=
 github.com/aws/aws-sdk-go-v2/service/kms v1.20.2/go.mod h1:vdqtUOdVuf5ooy+hJ2GnzqNo94xiAA9s1xbZ1hQgRE0=
 github.com/aws/aws-sdk-go-v2/service/kms v1.21.1 h1:Q03Jqh1enA8keCiGZpLetpk58Ll9iGejE5bOErxyGAU=
+github.com/aws/aws-sdk-go-v2/service/kms v1.21.1/go.mod h1:EEfb4gfSphdVpRo5sGf2W3KvJbelYUno5VaXR5MJ3z4=
 github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2 h1:5EQWIFO+Hc8E2hFcXQJ1vm6ufl/PMt/6RVRDZRju2vM=
 github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2/go.mod h1:SXDHd6fI2RhqB7vmAzyYQCTQnpZrIprVJvYxpzW3JAM=
 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3/go.mod h1:hqPcyOuLU6yWIbLy3qMnQnmidgKuIEwqIlW6+chYnog=
@@ -600,10 +616,13 @@ github.com/aws/aws-sdk-go-v2/service/sqs v1.20.2/go.mod h1:1ttxGjUHZliCQMpPss1sU
 github.com/aws/aws-sdk-go-v2/service/ssm v1.35.2/go.mod h1:VLSz2SHUKYFSOlXB/GlXoLU6KPYQJAbw7I20TDJdyws=
 github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI=
 github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY=
+github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk=
 github.com/aws/aws-sdk-go-v2/service/sts v1.18.3/go.mod h1:b+psTJn33Q4qGoDaM7ZiOVVG8uVjGI6HaZ8WBHdgDgU=
 github.com/aws/aws-sdk-go-v2/service/sts v1.18.11 h1:uBE+Zj478pfxV98L6SEpvxYiADNjTlMNY714PJLE7uo=
+github.com/aws/aws-sdk-go-v2/service/sts v1.18.11/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8=
 github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
 github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
 github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
@@ -640,6 +659,7 @@ github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oD
 github.com/cavaliercoder/go-rpm v0.0.0-20200122174316-8cb9fd9c31a8 h1:jP7ki8Tzx9ThnFPLDhBYAhEpI2+jOURnHQNURgsMvnY=
 github.com/cavaliercoder/go-rpm v0.0.0-20200122174316-8cb9fd9c31a8/go.mod h1:AZIh1CCnMrcVm6afFf96PBvE2MRpWFco91z8ObJtgDY=
 github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
+github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
 github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
 github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
 github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
@@ -841,6 +861,7 @@ github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8
 github.com/digitalocean/godo v1.78.0/go.mod h1:GBmu8MkjZmNARE7IXRPmkbbnocNN8+uBm0xbEVw2LCs=
 github.com/digitalocean/godo v1.95.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA=
 github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
+github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
 github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
 github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
@@ -941,6 +962,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
 github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
+github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
@@ -1085,13 +1107,13 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
 github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
-github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
+github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
 github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
@@ -1305,7 +1327,10 @@ github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa
 github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
 github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
 github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs=
+github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
+github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
+github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
 github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
 github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
 github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
@@ -1337,6 +1362,7 @@ github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpT
 github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
 github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
 github.com/hashicorp/vault/api v1.9.1 h1:LtY/I16+5jVGU8rufyyAkwopgq/HpUnxFBg+QLOAV38=
+github.com/hashicorp/vault/api v1.9.1/go.mod h1:78kktNcQYbBGSrOjQfHjXN32OhhxXnbYl3zxpd2uPUs=
 github.com/hetznercloud/hcloud-go v1.33.1/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME=
 github.com/hetznercloud/hcloud-go v1.39.0/go.mod h1:mepQwR6va27S3UQthaEPGS86jtzSY9xWL1e9dyxXpgA=
 github.com/honeycombio/beeline-go v1.10.0 h1:cUDe555oqvw8oD76BQJ8alk7FP0JZ/M/zXpNvOEDLDc=
@@ -1795,6 +1821,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
+github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
 github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
 github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
 github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
@@ -2103,6 +2130,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
diff --git a/openapi.yaml b/openapi.yaml
index 5b5e960..07f9367 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -426,6 +426,23 @@ definitions:
         - spec
       additionalProperties: false
 
+  dsse:
+    type: object
+    description: DSSE envelope
+    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/dsse/dsse_schema.json'
+      required:
+        - apiVersion
+        - spec
+      additionalProperties: false
+
   LogEntry:
     type: object
     additionalProperties:
diff --git a/pkg/generated/models/dsse.go b/pkg/generated/models/dsse.go
new file mode 100644
index 0000000..dde5620
--- /dev/null
+++ b/pkg/generated/models/dsse.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"
+)
+
+// DSSE DSSE envelope
+//
+// swagger:model dsse
+type DSSE 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 DSSESchema `json:"spec"`
+}
+
+// Kind gets the kind of this subtype
+func (m *DSSE) Kind() string {
+	return "dsse"
+}
+
+// SetKind sets the kind of this subtype
+func (m *DSSE) SetKind(val string) {
+}
+
+// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
+func (m *DSSE) 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 DSSESchema `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 DSSE
+
+	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 DSSE) 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 DSSESchema `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 dsse
+func (m *DSSE) 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 *DSSE) 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 *DSSE) validateSpec(formats strfmt.Registry) error {
+
+	if m.Spec == nil {
+		return errors.Required("spec", "body", nil)
+	}
+
+	return nil
+}
+
+// ContextValidate validate this dsse based on the context it is used
+func (m *DSSE) 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 *DSSE) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *DSSE) UnmarshalBinary(b []byte) error {
+	var res DSSE
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
diff --git a/pkg/generated/models/dsse_schema.go b/pkg/generated/models/dsse_schema.go
new file mode 100644
index 0000000..7795626
--- /dev/null
+++ b/pkg/generated/models/dsse_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
+
+// DSSESchema DSSE Schema
+//
+// log entry schema for dsse envelopes
+//
+// swagger:model dsseSchema
+type DSSESchema interface{}
diff --git a/pkg/generated/models/dsse_v001_schema.go b/pkg/generated/models/dsse_v001_schema.go
new file mode 100644
index 0000000..a28dd52
--- /dev/null
+++ b/pkg/generated/models/dsse_v001_schema.go
@@ -0,0 +1,665 @@
+// 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"
+	"strconv"
+
+	"github.com/go-openapi/errors"
+	"github.com/go-openapi/strfmt"
+	"github.com/go-openapi/swag"
+	"github.com/go-openapi/validate"
+)
+
+// DSSEV001Schema DSSE v0.0.1 Schema
+//
+// # Schema for DSSE envelopes
+//
+// swagger:model dsseV001Schema
+type DSSEV001Schema struct {
+
+	// envelope hash
+	EnvelopeHash *DSSEV001SchemaEnvelopeHash `json:"envelopeHash,omitempty"`
+
+	// payload hash
+	PayloadHash *DSSEV001SchemaPayloadHash `json:"payloadHash,omitempty"`
+
+	// proposed content
+	ProposedContent *DSSEV001SchemaProposedContent `json:"proposedContent,omitempty"`
+
+	// extracted collection of all signatures of the envelope's payload; elements will be sorted by lexicographical order of the base64 encoded signature strings
+	// Read Only: true
+	// Min Items: 1
+	Signatures []*DSSEV001SchemaSignaturesItems0 `json:"signatures"`
+}
+
+// Validate validates this dsse v001 schema
+func (m *DSSEV001Schema) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validateEnvelopeHash(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validatePayloadHash(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validateProposedContent(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validateSignatures(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *DSSEV001Schema) validateEnvelopeHash(formats strfmt.Registry) error {
+	if swag.IsZero(m.EnvelopeHash) { // not required
+		return nil
+	}
+
+	if m.EnvelopeHash != nil {
+		if err := m.EnvelopeHash.Validate(formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("envelopeHash")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("envelopeHash")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (m *DSSEV001Schema) validatePayloadHash(formats strfmt.Registry) error {
+	if swag.IsZero(m.PayloadHash) { // not required
+		return nil
+	}
+
+	if m.PayloadHash != nil {
+		if err := m.PayloadHash.Validate(formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("payloadHash")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("payloadHash")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (m *DSSEV001Schema) validateProposedContent(formats strfmt.Registry) error {
+	if swag.IsZero(m.ProposedContent) { // not required
+		return nil
+	}
+
+	if m.ProposedContent != nil {
+		if err := m.ProposedContent.Validate(formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("proposedContent")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("proposedContent")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (m *DSSEV001Schema) validateSignatures(formats strfmt.Registry) error {
+	if swag.IsZero(m.Signatures) { // not required
+		return nil
+	}
+
+	iSignaturesSize := int64(len(m.Signatures))
+
+	if err := validate.MinItems("signatures", "body", iSignaturesSize, 1); err != nil {
+		return err
+	}
+
+	for i := 0; i < len(m.Signatures); i++ {
+		if swag.IsZero(m.Signatures[i]) { // not required
+			continue
+		}
+
+		if m.Signatures[i] != nil {
+			if err := m.Signatures[i].Validate(formats); err != nil {
+				if ve, ok := err.(*errors.Validation); ok {
+					return ve.ValidateName("signatures" + "." + strconv.Itoa(i))
+				} else if ce, ok := err.(*errors.CompositeError); ok {
+					return ce.ValidateName("signatures" + "." + strconv.Itoa(i))
+				}
+				return err
+			}
+		}
+
+	}
+
+	return nil
+}
+
+// ContextValidate validate this dsse v001 schema based on the context it is used
+func (m *DSSEV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.contextValidateEnvelopeHash(ctx, formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.contextValidatePayloadHash(ctx, formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.contextValidateProposedContent(ctx, formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.contextValidateSignatures(ctx, formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *DSSEV001Schema) contextValidateEnvelopeHash(ctx context.Context, formats strfmt.Registry) error {
+
+	if m.EnvelopeHash != nil {
+		if err := m.EnvelopeHash.ContextValidate(ctx, formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("envelopeHash")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("envelopeHash")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (m *DSSEV001Schema) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error {
+
+	if m.PayloadHash != nil {
+		if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("payloadHash")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("payloadHash")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (m *DSSEV001Schema) contextValidateProposedContent(ctx context.Context, formats strfmt.Registry) error {
+
+	if m.ProposedContent != nil {
+		if err := m.ProposedContent.ContextValidate(ctx, formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("proposedContent")
+			} else if ce, ok := err.(*errors.CompositeError); ok {
+				return ce.ValidateName("proposedContent")
+			}
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (m *DSSEV001Schema) contextValidateSignatures(ctx context.Context, formats strfmt.Registry) error {
+
+	if err := validate.ReadOnly(ctx, "signatures", "body", []*DSSEV001SchemaSignaturesItems0(m.Signatures)); err != nil {
+		return err
+	}
+
+	for i := 0; i < len(m.Signatures); i++ {
+
+		if m.Signatures[i] != nil {
+			if err := m.Signatures[i].ContextValidate(ctx, formats); err != nil {
+				if ve, ok := err.(*errors.Validation); ok {
+					return ve.ValidateName("signatures" + "." + strconv.Itoa(i))
+				} else if ce, ok := err.(*errors.CompositeError); ok {
+					return ce.ValidateName("signatures" + "." + strconv.Itoa(i))
+				}
+				return err
+			}
+		}
+
+	}
+
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *DSSEV001Schema) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *DSSEV001Schema) UnmarshalBinary(b []byte) error {
+	var res DSSEV001Schema
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
+
+// DSSEV001SchemaEnvelopeHash Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor
+//
+// swagger:model DSSEV001SchemaEnvelopeHash
+type DSSEV001SchemaEnvelopeHash struct {
+
+	// The hashing function used to compute the hash value
+	// Required: true
+	// Enum: [sha256]
+	Algorithm *string `json:"algorithm"`
+
+	// The value of the computed digest over the entire envelope
+	// Required: true
+	Value *string `json:"value"`
+}
+
+// Validate validates this DSSE v001 schema envelope hash
+func (m *DSSEV001SchemaEnvelopeHash) 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 dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum []interface{}
+
+func init() {
+	var res []string
+	if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
+		panic(err)
+	}
+	for _, v := range res {
+		dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum = append(dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum, v)
+	}
+}
+
+const (
+
+	// DSSEV001SchemaEnvelopeHashAlgorithmSha256 captures enum value "sha256"
+	DSSEV001SchemaEnvelopeHashAlgorithmSha256 string = "sha256"
+)
+
+// prop value enum
+func (m *DSSEV001SchemaEnvelopeHash) validateAlgorithmEnum(path, location string, value string) error {
+	if err := validate.EnumCase(path, location, value, dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum, true); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (m *DSSEV001SchemaEnvelopeHash) validateAlgorithm(formats strfmt.Registry) error {
+
+	if err := validate.Required("envelopeHash"+"."+"algorithm", "body", m.Algorithm); err != nil {
+		return err
+	}
+
+	// value enum
+	if err := m.validateAlgorithmEnum("envelopeHash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (m *DSSEV001SchemaEnvelopeHash) validateValue(formats strfmt.Registry) error {
+
+	if err := validate.Required("envelopeHash"+"."+"value", "body", m.Value); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// ContextValidate validate this DSSE v001 schema envelope hash based on the context it is used
+func (m *DSSEV001SchemaEnvelopeHash) 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 *DSSEV001SchemaEnvelopeHash) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *DSSEV001SchemaEnvelopeHash) UnmarshalBinary(b []byte) error {
+	var res DSSEV001SchemaEnvelopeHash
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
+
+// DSSEV001SchemaPayloadHash Specifies the hash algorithm and value covering the payload within the DSSE envelope
+//
+// swagger:model DSSEV001SchemaPayloadHash
+type DSSEV001SchemaPayloadHash struct {
+
+	// The hashing function used to compute the hash value
+	// Required: true
+	// Enum: [sha256]
+	Algorithm *string `json:"algorithm"`
+
+	// The value of the computed digest over the payload within the envelope
+	// Required: true
+	Value *string `json:"value"`
+}
+
+// Validate validates this DSSE v001 schema payload hash
+func (m *DSSEV001SchemaPayloadHash) 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 dsseV001SchemaPayloadHashTypeAlgorithmPropEnum []interface{}
+
+func init() {
+	var res []string
+	if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
+		panic(err)
+	}
+	for _, v := range res {
+		dsseV001SchemaPayloadHashTypeAlgorithmPropEnum = append(dsseV001SchemaPayloadHashTypeAlgorithmPropEnum, v)
+	}
+}
+
+const (
+
+	// DSSEV001SchemaPayloadHashAlgorithmSha256 captures enum value "sha256"
+	DSSEV001SchemaPayloadHashAlgorithmSha256 string = "sha256"
+)
+
+// prop value enum
+func (m *DSSEV001SchemaPayloadHash) validateAlgorithmEnum(path, location string, value string) error {
+	if err := validate.EnumCase(path, location, value, dsseV001SchemaPayloadHashTypeAlgorithmPropEnum, true); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (m *DSSEV001SchemaPayloadHash) validateAlgorithm(formats strfmt.Registry) error {
+
+	if err := validate.Required("payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil {
+		return err
+	}
+
+	// value enum
+	if err := m.validateAlgorithmEnum("payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (m *DSSEV001SchemaPayloadHash) validateValue(formats strfmt.Registry) error {
+
+	if err := validate.Required("payloadHash"+"."+"value", "body", m.Value); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// ContextValidate validate this DSSE v001 schema payload hash based on the context it is used
+func (m *DSSEV001SchemaPayloadHash) 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 *DSSEV001SchemaPayloadHash) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *DSSEV001SchemaPayloadHash) UnmarshalBinary(b []byte) error {
+	var res DSSEV001SchemaPayloadHash
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
+
+// DSSEV001SchemaProposedContent DSSE v001 schema proposed content
+//
+// swagger:model DSSEV001SchemaProposedContent
+type DSSEV001SchemaProposedContent struct {
+
+	// DSSE envelope specified as a stringified JSON object
+	// Required: true
+	Envelope *string `json:"envelope"`
+
+	// collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings
+	// Required: true
+	// Min Items: 1
+	Verifiers []strfmt.Base64 `json:"verifiers"`
+}
+
+// Validate validates this DSSE v001 schema proposed content
+func (m *DSSEV001SchemaProposedContent) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validateEnvelope(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validateVerifiers(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *DSSEV001SchemaProposedContent) validateEnvelope(formats strfmt.Registry) error {
+
+	if err := validate.Required("proposedContent"+"."+"envelope", "body", m.Envelope); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (m *DSSEV001SchemaProposedContent) validateVerifiers(formats strfmt.Registry) error {
+
+	if err := validate.Required("proposedContent"+"."+"verifiers", "body", m.Verifiers); err != nil {
+		return err
+	}
+
+	iVerifiersSize := int64(len(m.Verifiers))
+
+	if err := validate.MinItems("proposedContent"+"."+"verifiers", "body", iVerifiersSize, 1); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// ContextValidate validates this DSSE v001 schema proposed content based on context it is used
+func (m *DSSEV001SchemaProposedContent) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *DSSEV001SchemaProposedContent) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *DSSEV001SchemaProposedContent) UnmarshalBinary(b []byte) error {
+	var res DSSEV001SchemaProposedContent
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
+
+// DSSEV001SchemaSignaturesItems0 a signature of the envelope's payload along with the verification material for the signature
+//
+// swagger:model DSSEV001SchemaSignaturesItems0
+type DSSEV001SchemaSignaturesItems0 struct {
+
+	// base64 encoded signature of the payload
+	// Required: true
+	// Pattern: ^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$
+	Signature *string `json:"signature"`
+
+	// verification material that was used to verify the corresponding signature, specified as a base64 encoded string
+	// Required: true
+	// Format: byte
+	Verifier *strfmt.Base64 `json:"verifier"`
+}
+
+// Validate validates this DSSE v001 schema signatures items0
+func (m *DSSEV001SchemaSignaturesItems0) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validateSignature(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validateVerifier(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *DSSEV001SchemaSignaturesItems0) validateSignature(formats strfmt.Registry) error {
+
+	if err := validate.Required("signature", "body", m.Signature); err != nil {
+		return err
+	}
+
+	if err := validate.Pattern("signature", "body", *m.Signature, `^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$`); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (m *DSSEV001SchemaSignaturesItems0) validateVerifier(formats strfmt.Registry) error {
+
+	if err := validate.Required("verifier", "body", m.Verifier); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// ContextValidate validates this DSSE v001 schema signatures items0 based on context it is used
+func (m *DSSEV001SchemaSignaturesItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *DSSEV001SchemaSignaturesItems0) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *DSSEV001SchemaSignaturesItems0) UnmarshalBinary(b []byte) error {
+	var res DSSEV001SchemaSignaturesItems0
+	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 76b2801..5b734a5 100644
--- a/pkg/generated/models/proposed_entry.go
+++ b/pkg/generated/models/proposed_entry.go
@@ -126,6 +126,12 @@ func unmarshalProposedEntry(data []byte, consumer runtime.Consumer) (ProposedEnt
 			return nil, err
 		}
 		return &result, nil
+	case "dsse":
+		var result DSSE
+		if err := consumer.Consume(buf2, &result); err != nil {
+			return nil, err
+		}
+		return &result, nil
 	case "hashedrekord":
 		var result Hashedrekord
 		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 2f7e5db..c49f253 100644
--- a/pkg/generated/restapi/embedded_spec.go
+++ b/pkg/generated/restapi/embedded_spec.go
@@ -688,6 +688,32 @@ func init() {
         }
       ]
     },
+    "dsse": {
+      "description": "DSSE envelope",
+      "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/dsse/dsse_schema.json"
+            }
+          },
+          "additionalProperties": false
+        }
+      ]
+    },
     "hashedrekord": {
       "description": "Hashed Rekord object",
       "type": "object",
@@ -1516,6 +1542,95 @@ func init() {
       },
       "readOnly": true
     },
+    "DSSEV001SchemaEnvelopeHash": {
+      "description": "Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor",
+      "type": "object",
+      "required": [
+        "algorithm",
+        "value"
+      ],
+      "properties": {
+        "algorithm": {
+          "description": "The hashing function used to compute the hash value",
+          "type": "string",
+          "enum": [
+            "sha256"
+          ]
+        },
+        "value": {
+          "description": "The value of the computed digest over the entire envelope",
+          "type": "string"
+        }
+      },
+      "readOnly": true
+    },
+    "DSSEV001SchemaPayloadHash": {
+      "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope",
+      "type": "object",
+      "required": [
+        "algorithm",
+        "value"
+      ],
+      "properties": {
+        "algorithm": {
+          "description": "The hashing function used to compute the hash value",
+          "type": "string",
+          "enum": [
+            "sha256"
+          ]
+        },
+        "value": {
+          "description": "The value of the computed digest over the payload within the envelope",
+          "type": "string"
+        }
+      },
+      "readOnly": true
+    },
+    "DSSEV001SchemaProposedContent": {
+      "type": "object",
+      "required": [
+        "envelope",
+        "verifiers"
+      ],
+      "properties": {
+        "envelope": {
+          "description": "DSSE envelope specified as a stringified JSON object",
+          "type": "string",
+          "writeOnly": true
+        },
+        "verifiers": {
+          "description": "collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings",
+          "type": "array",
+          "minItems": 1,
+          "items": {
+            "type": "string",
+            "format": "byte"
+          },
+          "writeOnly": true
+        }
+      },
+      "writeOnly": true
+    },
+    "DSSEV001SchemaSignaturesItems0": {
+      "description": "a signature of the envelope's payload along with the verification material for the signature",
+      "type": "object",
+      "required": [
+        "signature",
+        "verifier"
+      ],
+      "properties": {
+        "signature": {
+          "description": "base64 encoded signature of the payload",
+          "type": "string",
+          "pattern": "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
+        },
+        "verifier": {
+          "description": "verification material that was used to verify the corresponding signature, specified as a base64 encoded string",
+          "type": "string",
+          "format": "byte"
+        }
+      }
+    },
     "Error": {
       "type": "object",
       "properties": {
@@ -2941,6 +3056,144 @@ func init() {
       "$schema": "http://json-schema.org/draft-07/schema",
       "$id": "http://rekor.sigstore.dev/types/cose/cose_v0_0_1_schema.json"
     },
+    "dsse": {
+      "description": "DSSE envelope",
+      "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/dsseSchema"
+            }
+          },
+          "additionalProperties": false
+        }
+      ]
+    },
+    "dsseSchema": {
+      "description": "log entry schema for dsse envelopes",
+      "type": "object",
+      "title": "DSSE Schema",
+      "oneOf": [
+        {
+          "$ref": "#/definitions/dsseV001Schema"
+        }
+      ],
+      "$schema": "http://json-schema.org/draft-07/schema",
+      "$id": "http://rekor.sigstore.dev/types/dsse/dsse_schema.json"
+    },
+    "dsseV001Schema": {
+      "description": "Schema for DSSE envelopes",
+      "type": "object",
+      "title": "DSSE v0.0.1 Schema",
+      "oneOf": [
+        {
+          "required": [
+            "proposedContent"
+          ]
+        },
+        {
+          "required": [
+            "signatures",
+            "envelopeHash",
+            "payloadHash"
+          ]
+        }
+      ],
+      "properties": {
+        "envelopeHash": {
+          "description": "Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor",
+          "type": "object",
+          "required": [
+            "algorithm",
+            "value"
+          ],
+          "properties": {
+            "algorithm": {
+              "description": "The hashing function used to compute the hash value",
+              "type": "string",
+              "enum": [
+                "sha256"
+              ]
+            },
+            "value": {
+              "description": "The value of the computed digest over the entire envelope",
+              "type": "string"
+            }
+          },
+          "readOnly": true
+        },
+        "payloadHash": {
+          "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope",
+          "type": "object",
+          "required": [
+            "algorithm",
+            "value"
+          ],
+          "properties": {
+            "algorithm": {
+              "description": "The hashing function used to compute the hash value",
+              "type": "string",
+              "enum": [
+                "sha256"
+              ]
+            },
+            "value": {
+              "description": "The value of the computed digest over the payload within the envelope",
+              "type": "string"
+            }
+          },
+          "readOnly": true
+        },
+        "proposedContent": {
+          "type": "object",
+          "required": [
+            "envelope",
+            "verifiers"
+          ],
+          "properties": {
+            "envelope": {
+              "description": "DSSE envelope specified as a stringified JSON object",
+              "type": "string",
+              "writeOnly": true
+            },
+            "verifiers": {
+              "description": "collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings",
+              "type": "array",
+              "minItems": 1,
+              "items": {
+                "type": "string",
+                "format": "byte"
+              },
+              "writeOnly": true
+            }
+          },
+          "writeOnly": true
+        },
+        "signatures": {
+          "description": "extracted collection of all signatures of the envelope's payload; elements will be sorted by lexicographical order of the base64 encoded signature strings",
+          "type": "array",
+          "minItems": 1,
+          "items": {
+            "$ref": "#/definitions/DSSEV001SchemaSignaturesItems0"
+          },
+          "readOnly": true
+        }
+      },
+      "$schema": "http://json-schema.org/draft-07/schema",
+      "$id": "http://rekor.sigstore.dev/types/dsse/dsse_v0_0_1_schema.json"
+    },
     "hashedrekord": {
       "description": "Hashed Rekord object",
       "type": "object",
diff --git a/pkg/signer/signer.go b/pkg/signer/signer.go
index 481c184..6701e8c 100644
--- a/pkg/signer/signer.go
+++ b/pkg/signer/signer.go
@@ -1,5 +1,5 @@
 /*
-Copyright The Rekor Authors.
+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.
@@ -18,16 +18,27 @@ package signer
 
 import (
 	"context"
+	"crypto"
 	"strings"
 
 	"github.com/sigstore/sigstore/pkg/signature"
-	"github.com/sigstore/sigstore/pkg/signature/kms/gcp"
+	"github.com/sigstore/sigstore/pkg/signature/kms"
+	"golang.org/x/exp/slices"
+
+	// these are imported to load the providers via init() calls
+	_ "github.com/sigstore/sigstore/pkg/signature/kms/aws"
+	_ "github.com/sigstore/sigstore/pkg/signature/kms/azure"
+	_ "github.com/sigstore/sigstore/pkg/signature/kms/gcp"
+	_ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault"
 )
 
 func New(ctx context.Context, signer string, pass string) (signature.Signer, error) {
 	switch {
-	case strings.HasPrefix(signer, gcp.ReferenceScheme):
-		return gcp.LoadSignerVerifier(ctx, signer)
+	case slices.ContainsFunc(kms.SupportedProviders(),
+		func(s string) bool {
+			return strings.HasPrefix(signer, s)
+		}):
+		return kms.Get(ctx, signer, crypto.SHA256)
 	case signer == MemoryScheme:
 		return NewMemory()
 	default:
diff --git a/pkg/types/README.md b/pkg/types/README.md
index 81246c4..76cb36e 100644
--- a/pkg/types/README.md
+++ b/pkg/types/README.md
@@ -10,12 +10,14 @@ Rekor supports pluggable types (aka different schemas) for entries stored in the
   - Versions: 0.0.1
 - COSE Envelopes [schema](cose/cose_schema.json)
   - Versions: 0.0.1
+- DSSE Envelopes [schema](dsse/dsse_schema.json)
+  - Versions: 0.0.1
 - HashedRekord [schema](hashedrekord/hashedrekord_schema.json)
   - Versions: 0.0.1
 - Helm Provenance Files [schema](helm/helm_schema.json)
   - Versions: 0.0.1
 - In-Toto Attestations [schema](intoto/intoto_schema.json)
-  - Versions: 0.0.1
+  - Versions: 0.0.1, 0.0.2
 - Java Archives (JAR Files) [schema](jar/jar_schema.json)
   - Versions: 0.0.1
 - Rekord *(default type)* [schema](rekord/rekord_schema.json)
diff --git a/pkg/types/dsse/README.md b/pkg/types/dsse/README.md
new file mode 100644
index 0000000..3244c98
--- /dev/null
+++ b/pkg/types/dsse/README.md
@@ -0,0 +1,25 @@
+**DSSE Type Data Documentation**
+
+This document provides a definition for each field that is not otherwise described in the [dsse
+schema](https://github.com/sigstore/rekor/blob/main/pkg/types/dsse/v0.0.1/dsse_v0_0_1_schema.json). This
+document also notes any additional information about the values
+associated with each field such as the format in which the data is
+stored and any necessary transformations.
+
+**How do you identify an object as an DSSE object?**
+
+The "Body" field will include an "dsseObj" field.
+
+**Recognized content types**
+
+- [in-toto
+  statements](https://github.com/in-toto/attestation/tree/main/spec#statement)
+  are recognized and parsed. The found subject hashes are indexed so
+  they can be searched for.
+
+**What data about the envelope is stored in Rekor**
+
+Only the hash of the payload (the content covered by the digital signature inside the envelope), the hash of the entire DSSE envelope (including signatures),
+the signature(s) and their corresponding verifying materials (e.g. public key(s) or certificates) are stored.
+
+Even if Rekor is configured to use attestation storage, the entire DSSE envelope will not be stored.
diff --git a/pkg/types/dsse/dsse.go b/pkg/types/dsse/dsse.go
new file mode 100644
index 0000000..9036fe5
--- /dev/null
+++ b/pkg/types/dsse/dsse.go
@@ -0,0 +1,74 @@
+//
+// Copyright 2023 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 dsse
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/types"
+)
+
+const (
+	KIND = "dsse"
+)
+
+type BaseDSSEType struct {
+	types.RekorType
+}
+
+func init() {
+	types.TypeMap.Store(KIND, New)
+}
+
+func New() types.TypeImpl {
+	bit := BaseDSSEType{}
+	bit.Kind = KIND
+	bit.VersionMap = VersionMap
+	return &bit
+}
+
+var VersionMap = types.NewSemVerEntryFactoryMap()
+
+func (it BaseDSSEType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
+	if pe == nil {
+		return nil, errors.New("proposed entry cannot be nil")
+	}
+
+	in, ok := pe.(*models.DSSE)
+	if !ok {
+		return nil, errors.New("cannot unmarshal non-DSSE types")
+	}
+
+	return it.VersionedUnmarshal(in, *in.APIVersion)
+}
+
+func (it *BaseDSSEType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
+	if version == "" {
+		version = it.DefaultVersion()
+	}
+	ei, err := it.VersionedUnmarshal(nil, version)
+	if err != nil {
+		return nil, fmt.Errorf("fetching DSSE version implementation: %w", err)
+	}
+	return ei.CreateFromArtifactProperties(ctx, props)
+}
+
+func (it BaseDSSEType) DefaultVersion() string {
+	return "0.0.1"
+}
diff --git a/pkg/types/dsse/dsse_schema.json b/pkg/types/dsse/dsse_schema.json
new file mode 100644
index 0000000..7dc710f
--- /dev/null
+++ b/pkg/types/dsse/dsse_schema.json
@@ -0,0 +1,12 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "$id": "http://rekor.sigstore.dev/types/dsse/dsse_schema.json",
+    "title": "DSSE Schema",
+    "description": "log entry schema for dsse envelopes",
+    "type": "object",
+    "oneOf": [
+        {
+            "$ref": "v0.0.1/dsse_v0_0_1_schema.json"
+        }
+    ]
+}
diff --git a/pkg/types/dsse/v0.0.1/dsse_v0_0_1_schema.json b/pkg/types/dsse/v0.0.1/dsse_v0_0_1_schema.json
new file mode 100644
index 0000000..51ebe39
--- /dev/null
+++ b/pkg/types/dsse/v0.0.1/dsse_v0_0_1_schema.json
@@ -0,0 +1,96 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "$id": "http://rekor.sigstore.dev/types/dsse/dsse_v0_0_1_schema.json",
+    "title": "DSSE v0.0.1 Schema",
+    "description": "Schema for DSSE envelopes",
+    "type": "object",
+    "properties": {
+        "proposedContent": {
+            "type": "object",
+            "properties": {
+                "envelope": {
+                    "description": "DSSE envelope specified as a stringified JSON object",
+                    "type": "string",
+                    "writeOnly": true
+                },
+                "verifiers": {
+                    "description": "collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings",
+                    "type": "array",
+                    "minItems": 1,
+                    "items": {
+                        "type": "string",
+                        "format": "byte"
+                    },
+                    "writeOnly": true
+                }
+            },
+            "writeOnly": true,
+            "required": [ "envelope", "verifiers" ]
+        },
+        "signatures": {
+            "description": "extracted collection of all signatures of the envelope's payload; elements will be sorted by lexicographical order of the base64 encoded signature strings",
+            "type": "array",
+            "minItems": 1,
+            "items": {
+                "description": "a signature of the envelope's payload along with the verification material for the signature",
+                "type": "object",
+                "properties": {
+                    "signature": {
+                        "description": "base64 encoded signature of the payload",
+                        "type": "string",
+                        "pattern": "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
+                    },
+                    "verifier": {
+                        "description": "verification material that was used to verify the corresponding signature, specified as a base64 encoded string",
+                        "type": "string",
+                        "format": "byte"
+                    }
+                },
+                "required": [ "signature", "verifier" ]
+            },
+            "readOnly": true
+        },
+        "envelopeHash": {
+            "description": "Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor",
+            "type": "object",
+            "properties": {
+                "algorithm": {
+                    "description": "The hashing function used to compute the hash value",
+                    "type": "string",
+                    "enum": [ "sha256" ]
+                },
+                "value": {
+                    "description": "The value of the computed digest over the entire envelope",
+                    "type": "string"
+                }
+            },
+            "required": [ "algorithm", "value" ],
+            "readOnly": true
+        },
+        "payloadHash": {
+            "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope",
+            "type": "object",
+            "properties": {
+                "algorithm": {
+                    "description": "The hashing function used to compute the hash value",
+                    "type": "string",
+                    "enum": [ "sha256" ]
+                },
+                "value": {
+                    "description": "The value of the computed digest over the payload within the envelope",
+                    "type": "string"
+                }
+            },
+            "required": [ "algorithm", "value" ],
+            "readOnly": true
+        }
+    },
+    "oneOf": [
+        {
+            "required": [ "proposedContent" ]
+        },
+        {
+            "required": [ "signatures", "envelopeHash", "payloadHash" ]
+        }
+    ]
+}
diff --git a/pkg/types/dsse/v0.0.1/entry.go b/pkg/types/dsse/v0.0.1/entry.go
new file mode 100644
index 0000000..2b73772
--- /dev/null
+++ b/pkg/types/dsse/v0.0.1/entry.go
@@ -0,0 +1,420 @@
+//
+// Copyright 2023 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 dsse
+
+import (
+	"bytes"
+	"context"
+	"crypto"
+	"crypto/sha256"
+	"encoding/hex"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"github.com/in-toto/in-toto-golang/in_toto"
+	"github.com/secure-systems-lab/go-securesystemslib/dsse"
+
+	"github.com/go-openapi/strfmt"
+	"github.com/go-openapi/swag"
+
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/log"
+	"github.com/sigstore/rekor/pkg/pki"
+	"github.com/sigstore/rekor/pkg/pki/x509"
+	"github.com/sigstore/rekor/pkg/types"
+	dsseType "github.com/sigstore/rekor/pkg/types/dsse"
+	"github.com/sigstore/sigstore/pkg/signature"
+	sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse"
+)
+
+const (
+	APIVERSION = "0.0.1"
+)
+
+func init() {
+	if err := dsseType.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
+		log.Logger.Panic(err)
+	}
+}
+
+type V001Entry struct {
+	DSSEObj models.DSSEV001Schema
+	env     *dsse.Envelope
+}
+
+func (v V001Entry) APIVersion() string {
+	return APIVERSION
+}
+
+func NewEntry() types.EntryImpl {
+	return &V001Entry{}
+}
+
+// IndexKeys computes the list of keys that should map back to this entry.
+// It should *never* reference v.DSSEObj.ProposedContent as those values would only
+// be present at the time of insertion
+func (v V001Entry) IndexKeys() ([]string, error) {
+	var result []string
+
+	for _, sig := range v.DSSEObj.Signatures {
+		keyObj, err := x509.NewPublicKey(bytes.NewReader(*sig.Verifier))
+		if err != nil {
+			return result, err
+		}
+
+		canonKey, err := keyObj.CanonicalValue()
+		if err != nil {
+			return result, fmt.Errorf("could not canonicalize key: %w", err)
+		}
+
+		keyHash := sha256.Sum256(canonKey)
+		result = append(result, "sha256:"+hex.EncodeToString(keyHash[:]))
+
+		result = append(result, keyObj.Subjects()...)
+	}
+
+	if v.DSSEObj.PayloadHash != nil {
+		payloadHashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.DSSEObj.PayloadHash.Algorithm, *v.DSSEObj.PayloadHash.Value))
+		result = append(result, payloadHashKey)
+	}
+
+	if v.DSSEObj.EnvelopeHash != nil {
+		envelopeHashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.DSSEObj.EnvelopeHash.Algorithm, *v.DSSEObj.EnvelopeHash.Value))
+		result = append(result, envelopeHashKey)
+	}
+
+	if v.env == nil {
+		log.Logger.Info("DSSEObj content or DSSE envelope is nil, returning partial set of keys")
+		return result, nil
+	}
+
+	switch v.env.PayloadType {
+	case in_toto.PayloadType:
+
+		if v.env.Payload == "" {
+			log.Logger.Info("DSSEObj DSSE payload is empty")
+			return result, nil
+		}
+		decodedPayload, err := v.env.DecodeB64Payload()
+		if err != nil {
+			return result, fmt.Errorf("could not decode envelope payload: %w", err)
+		}
+		statement, err := parseStatement(decodedPayload)
+		if err != nil {
+			return result, err
+		}
+		for _, s := range statement.Subject {
+			for alg, ds := range s.Digest {
+				result = append(result, alg+":"+ds)
+			}
+		}
+		// Not all in-toto statements will contain a SLSA provenance predicate.
+		// See https://github.com/in-toto/attestation/blob/main/spec/README.md#predicate
+		// for other predicates.
+		if predicate, err := parseSlsaPredicate(decodedPayload); err == nil {
+			if predicate.Predicate.Materials != nil {
+				for _, s := range predicate.Predicate.Materials {
+					for alg, ds := range s.Digest {
+						result = append(result, alg+":"+ds)
+					}
+				}
+			}
+		}
+	default:
+		log.Logger.Infof("Unknown DSSE envelope payloadType: %s", v.env.PayloadType)
+	}
+	return result, nil
+}
+
+func parseStatement(p []byte) (*in_toto.Statement, error) {
+	ps := in_toto.Statement{}
+	if err := json.Unmarshal(p, &ps); err != nil {
+		return nil, err
+	}
+	return &ps, nil
+}
+
+func parseSlsaPredicate(p []byte) (*in_toto.ProvenanceStatement, error) {
+	predicate := in_toto.ProvenanceStatement{}
+	if err := json.Unmarshal(p, &predicate); err != nil {
+		return nil, err
+	}
+	return &predicate, nil
+}
+
+func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
+	it, ok := pe.(*models.DSSE)
+	if !ok {
+		return errors.New("cannot unmarshal non DSSE v0.0.1 type")
+	}
+
+	dsseObj := &models.DSSEV001Schema{}
+
+	if err := types.DecodeEntry(it.Spec, dsseObj); err != nil {
+		return err
+	}
+
+	// field validation
+	if err := dsseObj.Validate(strfmt.Default); err != nil {
+		return err
+	}
+
+	// either we have just proposed content or the canonicalized fields
+	if dsseObj.ProposedContent == nil {
+		// then we need canonicalized fields, and all must be present (if present, they would have been validated in the above call to Validate())
+		if dsseObj.EnvelopeHash == nil || dsseObj.PayloadHash == nil || len(dsseObj.Signatures) == 0 {
+			return errors.New("either proposedContent or envelopeHash, payloadHash, and signatures must be present")
+		}
+		v.DSSEObj = *dsseObj
+		return nil
+	}
+	// if we're here, then we're trying to propose a new entry so we check to ensure client's aren't setting server-side computed fields
+	if dsseObj.EnvelopeHash != nil || dsseObj.PayloadHash != nil || len(dsseObj.Signatures) != 0 {
+		return errors.New("either proposedContent or envelopeHash, payloadHash, and signatures must be present but not both")
+	}
+
+	env := &dsse.Envelope{}
+	if err := json.Unmarshal([]byte(*dsseObj.ProposedContent.Envelope), env); err != nil {
+		return err
+	}
+
+	if len(env.Signatures) == 0 {
+		return errors.New("DSSE envelope must contain 1 or more signatures")
+	}
+
+	allPubKeyBytes := make([][]byte, 0)
+	for _, publicKey := range dsseObj.ProposedContent.Verifiers {
+		allPubKeyBytes = append(allPubKeyBytes, publicKey)
+	}
+
+	sigToKeyMap, err := verifyEnvelope(allPubKeyBytes, env)
+	if err != nil {
+		return err
+	}
+
+	// we need to ensure we canonicalize the ordering of signatures
+	sortedSigs := make([]string, 0, len(sigToKeyMap))
+	for sig := range sigToKeyMap {
+		sortedSigs = append(sortedSigs, sig)
+	}
+	sort.Strings(sortedSigs)
+
+	for i, sig := range sortedSigs {
+		key := sigToKeyMap[sig]
+		canonicalizedKey, err := key.CanonicalValue()
+		if err != nil {
+			return err
+		}
+		b64CanonicalizedKey := strfmt.Base64(canonicalizedKey)
+
+		dsseObj.Signatures = append(dsseObj.Signatures, &models.DSSEV001SchemaSignaturesItems0{
+			Signature: &sortedSigs[i],
+			Verifier:  &b64CanonicalizedKey,
+		})
+	}
+
+	decodedPayload, err := env.DecodeB64Payload()
+	if err != nil {
+		// this shouldn't happen because failure would have occurred in verifyEnvelope call above
+		return err
+	}
+
+	payloadHash := sha256.Sum256(decodedPayload)
+	dsseObj.PayloadHash = &models.DSSEV001SchemaPayloadHash{
+		Algorithm: swag.String(models.DSSEV001SchemaPayloadHashAlgorithmSha256),
+		Value:     swag.String(hex.EncodeToString(payloadHash[:])),
+	}
+
+	envelopeHash := sha256.Sum256([]byte(*dsseObj.ProposedContent.Envelope))
+	dsseObj.EnvelopeHash = &models.DSSEV001SchemaEnvelopeHash{
+		Algorithm: swag.String(models.DSSEV001SchemaEnvelopeHashAlgorithmSha256),
+		Value:     swag.String(hex.EncodeToString(envelopeHash[:])),
+	}
+
+	// we've gotten through all processing without error, now update the object we're unmarshalling into
+	v.DSSEObj = *dsseObj
+	v.env = env
+
+	return nil
+}
+
+// Canonicalize returns a JSON representation of the entry to be persisted into the log. This
+// will be further canonicalized by JSON Canonicalization Scheme (JCS) before being written.
+//
+// This function should not use v.DSSEObj.ProposedContent fields as they are client provided and
+// should not be trusted; the other fields at the top level are only set server side.
+func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) {
+	canonicalEntry := models.DSSEV001Schema{
+		Signatures:      v.DSSEObj.Signatures,
+		EnvelopeHash:    v.DSSEObj.EnvelopeHash,
+		PayloadHash:     v.DSSEObj.PayloadHash,
+		ProposedContent: nil, // this is explicitly done as we don't want to canonicalize the envelope
+	}
+
+	sort.Slice(canonicalEntry.Signatures, func(i, j int) bool {
+		return *canonicalEntry.Signatures[i].Signature < *canonicalEntry.Signatures[j].Signature
+	})
+
+	itObj := models.DSSE{}
+	itObj.APIVersion = swag.String(APIVERSION)
+	itObj.Spec = &canonicalEntry
+
+	return json.Marshal(&itObj)
+}
+
+// AttestationKey and AttestationKeyValue are not implemented so the envelopes will not be persisted in Rekor
+
+func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
+	returnVal := models.DSSE{}
+	re := V001Entry{
+		DSSEObj: models.DSSEV001Schema{
+			ProposedContent: &models.DSSEV001SchemaProposedContent{},
+		},
+	}
+	var err error
+	artifactBytes := props.ArtifactBytes
+	if artifactBytes == nil {
+		if props.ArtifactPath == nil {
+			return nil, errors.New("path to artifact file must be specified")
+		}
+		if props.ArtifactPath.IsAbs() {
+			return nil, errors.New("dsse envelopes cannot be fetched over HTTP(S)")
+		}
+		artifactBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path))
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	env := &dsse.Envelope{}
+	if err := json.Unmarshal(artifactBytes, env); err != nil {
+		return nil, fmt.Errorf("payload must be a valid DSSE envelope: %w", err)
+	}
+
+	allPubKeyBytes := make([][]byte, 0)
+	if len(props.PublicKeyBytes) > 0 {
+		allPubKeyBytes = append(allPubKeyBytes, props.PublicKeyBytes...)
+	}
+
+	if len(props.PublicKeyPaths) > 0 {
+		for _, path := range props.PublicKeyPaths {
+			if path.IsAbs() {
+				return nil, errors.New("dsse public keys cannot be fetched over HTTP(S)")
+			}
+
+			publicKeyBytes, err := os.ReadFile(filepath.Clean(path.Path))
+			if err != nil {
+				return nil, fmt.Errorf("error reading public key file: %w", err)
+			}
+
+			allPubKeyBytes = append(allPubKeyBytes, publicKeyBytes)
+		}
+	}
+
+	keysBySig, err := verifyEnvelope(allPubKeyBytes, env)
+	if err != nil {
+		return nil, err
+	}
+	for _, key := range keysBySig {
+		canonicalKey, err := key.CanonicalValue()
+		if err != nil {
+			return nil, err
+		}
+		re.DSSEObj.ProposedContent.Verifiers = append(re.DSSEObj.ProposedContent.Verifiers, strfmt.Base64(canonicalKey))
+	}
+	re.DSSEObj.ProposedContent.Envelope = swag.String(string(artifactBytes))
+
+	returnVal.Spec = re.DSSEObj
+	returnVal.APIVersion = swag.String(re.APIVersion())
+
+	return &returnVal, nil
+}
+
+// verifyEnvelope takes in an array of possible key bytes and attempts to parse them as x509 public keys.
+// it then uses these to verify the envelope and makes sure that every signature on the envelope is verified.
+// it returns a map of verifiers indexed by the signature the verifier corresponds to.
+func verifyEnvelope(allPubKeyBytes [][]byte, env *dsse.Envelope) (map[string]*x509.PublicKey, error) {
+	// generate a fake id for these keys so we can get back to the key bytes and match them to their corresponding signature
+	verifierBySig := make(map[string]*x509.PublicKey)
+	allSigs := make(map[string]struct{})
+	for _, sig := range env.Signatures {
+		allSigs[sig.Sig] = struct{}{}
+	}
+
+	for _, pubKeyBytes := range allPubKeyBytes {
+		if len(allSigs) == 0 {
+			break // if all signatures have been verified, do not attempt anymore
+		}
+		key, err := x509.NewPublicKey(bytes.NewReader(pubKeyBytes))
+		if err != nil {
+			return nil, fmt.Errorf("could not parse public key as x509: %w", err)
+		}
+
+		vfr, err := signature.LoadVerifier(key.CryptoPubKey(), crypto.SHA256)
+		if err != nil {
+			return nil, fmt.Errorf("could not load verifier: %w", err)
+		}
+
+		dsseVfr, err := dsse.NewEnvelopeVerifier(&sigdsse.VerifierAdapter{SignatureVerifier: vfr})
+		if err != nil {
+			return nil, fmt.Errorf("could not use public key as a dsse verifier: %w", err)
+		}
+
+		accepted, err := dsseVfr.Verify(context.Background(), env)
+		if err != nil {
+			return nil, fmt.Errorf("could not verify envelope: %w", err)
+		}
+
+		for _, accept := range accepted {
+			delete(allSigs, accept.Sig.Sig)
+			verifierBySig[accept.Sig.Sig] = key
+		}
+	}
+
+	if len(allSigs) > 0 {
+		return nil, errors.New("all signatures must have a key that verifies it")
+	}
+
+	return verifierBySig, nil
+}
+
+func (v V001Entry) Verifier() (pki.PublicKey, error) {
+	if len(v.DSSEObj.Signatures) == 0 {
+		return nil, errors.New("dsse v0.0.1 entry not initialized")
+	}
+
+	//TODO: return multiple pki.PublicKeys; sigstore/rekor issue #1278
+	return x509.NewPublicKey(bytes.NewReader(*v.DSSEObj.Signatures[0].Verifier))
+}
+
+func (v V001Entry) Insertable() (bool, error) {
+	if v.DSSEObj.ProposedContent == nil {
+		return false, errors.New("missing proposed content")
+	}
+	if v.DSSEObj.ProposedContent.Envelope == nil || len(*v.DSSEObj.ProposedContent.Envelope) == 0 {
+		return false, errors.New("missing proposed DSSE envelope")
+	}
+	if len(v.DSSEObj.ProposedContent.Verifiers) == 0 {
+		return false, errors.New("missing proposed verifiers")
+	}
+
+	return true, nil
+}
diff --git a/pkg/types/intoto/v0.0.1/entry.go b/pkg/types/intoto/v0.0.1/entry.go
index 66af807..1018d4c 100644
--- a/pkg/types/intoto/v0.0.1/entry.go
+++ b/pkg/types/intoto/v0.0.1/entry.go
@@ -193,9 +193,8 @@ func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) {
 	if v.IntotoObj.Content.Hash == nil {
 		return nil, errors.New("missing envelope hash")
 	}
-	if v.IntotoObj.Content.PayloadHash == nil {
-		return nil, errors.New("missing payload hash")
-	}
+	// PayloadHash is not present for old entries
+
 	pk, err := v.keyObj.CanonicalValue()
 	if err != nil {
 		return nil, err
@@ -209,12 +208,15 @@ func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) {
 				Algorithm: v.IntotoObj.Content.Hash.Algorithm,
 				Value:     v.IntotoObj.Content.Hash.Value,
 			},
-			PayloadHash: &models.IntotoV001SchemaContentPayloadHash{
-				Algorithm: v.IntotoObj.Content.PayloadHash.Algorithm,
-				Value:     v.IntotoObj.Content.PayloadHash.Value,
-			},
 		},
 	}
+	// Set PayloadHash if present
+	if v.IntotoObj.Content.PayloadHash != nil {
+		canonicalEntry.Content.PayloadHash = &models.IntotoV001SchemaContentPayloadHash{
+			Algorithm: v.IntotoObj.Content.PayloadHash.Algorithm,
+			Value:     v.IntotoObj.Content.PayloadHash.Value,
+		}
+	}
 
 	itObj := models.Intoto{}
 	itObj.APIVersion = swag.String(APIVERSION)
@@ -237,10 +239,11 @@ func (v *V001Entry) validate() error {
 		} else if err := v.IntotoObj.Content.Hash.Validate(strfmt.Default); err != nil {
 			return fmt.Errorf("validation error on envelope hash: %w", err)
 		}
-		if v.IntotoObj.Content.PayloadHash == nil {
-			return fmt.Errorf("missing hash value for payload")
-		} else if err := v.IntotoObj.Content.PayloadHash.Validate(strfmt.Default); err != nil {
-			return fmt.Errorf("validation error on payload hash: %w", err)
+		// PayloadHash is not present for old entries
+		if v.IntotoObj.Content.PayloadHash != nil {
+			if err := v.IntotoObj.Content.PayloadHash.Validate(strfmt.Default); err != nil {
+				return fmt.Errorf("validation error on payload hash: %w", err)
+			}
 		}
 		// if there is no envelope, and hash/payloadHash are valid, then there's nothing else to do here
 		return nil


Test patch:
diff --git a/pkg/types/dsse/dsse_test.go b/pkg/types/dsse/dsse_test.go
new file mode 100644
index 0000000..c8579b4
--- /dev/null
+++ b/pkg/types/dsse/dsse_test.go
@@ -0,0 +1,173 @@
+//
+// Copyright 2023 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 dsse
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/go-openapi/swag"
+
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/pki"
+	"github.com/sigstore/rekor/pkg/types"
+)
+
+type UnmarshalTester struct {
+	models.DSSE
+	types.BaseUnmarshalTester
+}
+
+type UnmarshalFailsTester struct {
+	types.BaseUnmarshalTester
+}
+
+func (u UnmarshalFailsTester) NewEntry() types.EntryImpl {
+	return &UnmarshalFailsTester{}
+}
+
+func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error {
+	return errors.New("error")
+}
+
+func (u UnmarshalFailsTester) Verifier() (pki.PublicKey, error) {
+	return nil, nil
+}
+
+func TestDSSEType(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.DSSE.APIVersion = swag.String("2.0.1")
+	brt := New()
+
+	// version requested matches implementation in map
+	if _, err := brt.UnmarshalEntry(&u.DSSE); err != nil {
+		t.Errorf("unexpected error in Unmarshal: %v", err)
+	}
+
+	// version requested fails to match implementation in map
+	u.DSSE.APIVersion = swag.String("1.2.2")
+	if _, err := brt.UnmarshalEntry(&u.DSSE); err == nil {
+		t.Error("unexpected success in Unmarshal for non-matching version")
+	}
+
+	// error in Unmarshal call is raised appropriately
+	u.DSSE.APIVersion = swag.String("2.2.0")
+	u2 := UnmarshalFailsTester{}
+	_ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry)
+	if _, err := brt.UnmarshalEntry(&u.DSSE); err == nil {
+		t.Error("unexpected success in Unmarshal when error is thrown")
+	}
+
+	// version requested fails to match implementation in map
+	u.DSSE.APIVersion = swag.String("not_a_version")
+	if _, err := brt.UnmarshalEntry(&u.DSSE); err == nil {
+		t.Error("unexpected success in Unmarshal for invalid version")
+	}
+
+	ti, err := brt.UnmarshalEntry(nil)
+	if ti != nil {
+		t.Error("unexpected success in unmarshal for nil")
+	}
+	if err == nil {
+		t.Error("expected error")
+	}
+
+	ti, err = brt.UnmarshalEntry(types.BaseProposedEntryTester{})
+	if ti != nil {
+		t.Error("unexpected success in unmarshal for nil")
+	}
+	if err == nil {
+		t.Error("expected error")
+	}
+
+}
+
+func TestDSSEDefaultVersion(t *testing.T) {
+	brt := New()
+	ver := brt.DefaultVersion()
+	if ver != "0.0.1" {
+		t.Errorf("unexpected default version %s", ver)
+	}
+}
+
+func TestDSSECreateProposedEntry(t *testing.T) {
+	// Reset semver map
+	VersionMap = types.NewSemVerEntryFactoryMap()
+	u := UnmarshalTester{}
+	VersionMap.SetEntryFactory("0.0.1", u.NewEntry)
+	VersionMap.SetEntryFactory(New().DefaultVersion(), u.NewEntry)
+
+	t.Run("unknown version", func(t *testing.T) {
+		ctx := context.Background()
+		brt := New()
+		props := types.ArtifactProperties{}
+		pe, err := brt.CreateProposedEntry(ctx, "1.2.3", props)
+
+		if pe != nil {
+			t.Error("unexpected proposed entry")
+		}
+		if err == nil {
+			t.Error("expected error")
+		}
+	})
+	t.Run("valid version", func(t *testing.T) {
+		ctx := context.Background()
+		brt := New()
+		props := types.ArtifactProperties{}
+		pe, err := brt.CreateProposedEntry(ctx, "0.0.1", props)
+
+		// BaseUnmarshalTester returns nil for the proposed entry
+		if pe != nil {
+			t.Error("unexpected proposed entry")
+		}
+		if err != nil {
+			t.Error("unexpected error")
+		}
+	})
+	t.Run("default version", func(t *testing.T) {
+		ctx := context.Background()
+		brt := New()
+		props := types.ArtifactProperties{}
+		pe, err := brt.CreateProposedEntry(ctx, "", props)
+
+		// BaseUnmarshalTester returns nil for the proposed entry
+		if pe != nil {
+			t.Error("unexpected proposed entry")
+		}
+		if err != nil {
+			t.Error("unexpected error")
+		}
+	})
+}
diff --git a/pkg/types/dsse/v0.0.1/e2e_test.go b/pkg/types/dsse/v0.0.1/e2e_test.go
new file mode 100644
index 0000000..aa67775
--- /dev/null
+++ b/pkg/types/dsse/v0.0.1/e2e_test.go
@@ -0,0 +1,723 @@
+//
+// Copyright 2023 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.
+
+//go:build e2e
+
+package dsse
+
+import (
+	"bytes"
+	"context"
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"encoding/pem"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/go-openapi/runtime"
+	"github.com/go-openapi/strfmt"
+	"github.com/go-openapi/swag"
+	"github.com/in-toto/in-toto-golang/in_toto"
+	"github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
+	slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
+	"github.com/secure-systems-lab/go-securesystemslib/dsse"
+	"github.com/sigstore/rekor/pkg/client"
+	"github.com/sigstore/rekor/pkg/generated/client/entries"
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/types"
+	"github.com/sigstore/sigstore/pkg/signature"
+
+	sigx509 "github.com/sigstore/rekor/pkg/pki/x509"
+	"github.com/sigstore/rekor/pkg/util"
+)
+
+func rekorServer() string {
+	if s := os.Getenv("REKOR_SERVER"); s != "" {
+		return s
+	}
+	return "http://localhost:3000"
+}
+
+func GenerateSingleSignedDSSE(t *testing.T) ([]byte, []byte) {
+	t.Helper()
+
+	// Get some random data so it's unique each run
+	d := util.RandomData(t, 10)
+	id := base64.StdEncoding.EncodeToString(d)
+
+	it := in_toto.ProvenanceStatement{
+		StatementHeader: in_toto.StatementHeader{
+			Type:          in_toto.StatementInTotoV01,
+			PredicateType: slsa.PredicateSLSAProvenance,
+			Subject: []in_toto.Subject{
+				{
+					Name: "foobar",
+					Digest: common.DigestSet{
+						"foo": "bar",
+					},
+				},
+			},
+		},
+		Predicate: slsa.ProvenancePredicate{
+			Builder: common.ProvenanceBuilder{
+				ID: "foo" + id,
+			},
+		},
+	}
+
+	b, err := json.Marshal(it)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	pb, _ := pem.Decode([]byte(sigx509.ECDSAPriv))
+	priv, err := x509.ParsePKCS8PrivateKey(pb.Bytes)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	s, err := signature.LoadECDSASigner(priv.(*ecdsa.PrivateKey), crypto.SHA256)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	signer, err := dsse.NewEnvelopeSigner(&sigx509.Verifier{
+		S: s,
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	env, err := signer.SignPayload(context.Background(), in_toto.PayloadType, b)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	eb, err := json.Marshal(env)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	return b, eb
+}
+
+func GenerateDoubleSignedDSSE(t *testing.T) ([]byte, []byte) {
+	t.Helper()
+
+	// Get some random data so it's unique each run
+	d := util.RandomData(t, 10)
+	id := base64.StdEncoding.EncodeToString(d)
+
+	it := in_toto.ProvenanceStatement{
+		StatementHeader: in_toto.StatementHeader{
+			Type:          in_toto.StatementInTotoV01,
+			PredicateType: slsa.PredicateSLSAProvenance,
+			Subject: []in_toto.Subject{
+				{
+					Name: "foobar",
+					Digest: common.DigestSet{
+						"foo": "bar",
+					},
+				},
+			},
+		},
+		Predicate: slsa.ProvenancePredicate{
+			Builder: common.ProvenanceBuilder{
+				ID: "foo" + id,
+			},
+		},
+	}
+
+	b, err := json.Marshal(it)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	evps := []*sigx509.Verifier{}
+
+	pb, _ := pem.Decode([]byte(sigx509.ECDSAPriv))
+	priv, err := x509.ParsePKCS8PrivateKey(pb.Bytes)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	signECDSA, err := signature.LoadECDSASigner(priv.(*ecdsa.PrivateKey), crypto.SHA256)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	evps = append(evps, &sigx509.Verifier{
+		S: signECDSA,
+	})
+
+	pbRSA, _ := pem.Decode([]byte(sigx509.RSAKey))
+	rsaPriv, err := x509.ParsePKCS8PrivateKey(pbRSA.Bytes)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	signRSA, err := signature.LoadRSAPKCS1v15Signer(rsaPriv.(*rsa.PrivateKey), crypto.SHA256)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	evps = append(evps, &sigx509.Verifier{
+		S: signRSA,
+	})
+
+	signer, err := dsse.NewMultiEnvelopeSigner(2, evps[0], evps[1])
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	env, err := signer.SignPayload(context.Background(), in_toto.PayloadType, b)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	eb, err := json.Marshal(env)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	return b, eb
+}
+
+func TestDsse(t *testing.T) {
+	td := t.TempDir()
+	attestationPath := filepath.Join(td, "attestation.json")
+	pubKeyPath := filepath.Join(td, "pub.pem")
+
+	b, eb := GenerateSingleSignedDSSE(t)
+
+	util.Write(t, string(eb), attestationPath)
+	util.Write(t, sigx509.ECDSAPub, pubKeyPath)
+
+	out := util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "dsse", "--public-key", pubKeyPath)
+	util.OutputContains(t, out, "Created entry at")
+	uuid := util.GetUUIDFromUploadOutput(t, out)
+
+	out = util.RunCli(t, "get", "--uuid", uuid, "--format=json")
+	g := util.GetOut{}
+	if err := json.Unmarshal([]byte(out), &g); err != nil {
+		t.Fatal(err)
+	}
+	// The attestation should not be stored
+
+	if len(g.Attestation) > 0 {
+		t.Fatal("unexpected attestation present in response")
+	}
+
+	payloadHash := sha256.Sum256(b)
+	envelopeHash := sha256.Sum256(eb)
+
+	dsseModel := &models.DSSEV001Schema{}
+	if err := types.DecodeEntry(g.Body.(map[string]interface{})["DSSEObj"], dsseModel); err != nil {
+		t.Errorf("could not convert body into dsse type: %v", err)
+	}
+	if dsseModel.PayloadHash == nil {
+		t.Errorf("could not find hash over payload %v", dsseModel)
+	}
+	recordedPayloadHash, err := hex.DecodeString(*dsseModel.PayloadHash.Value)
+	if err != nil {
+		t.Errorf("error converting payload hash to []byte: %v", err)
+	}
+
+	if !bytes.Equal(payloadHash[:], recordedPayloadHash) {
+		t.Fatal(fmt.Errorf("payload hash %v doesnt match the payload we sent %v", hex.EncodeToString(payloadHash[:]),
+			*dsseModel.PayloadHash.Value))
+	}
+	if dsseModel.EnvelopeHash == nil {
+		t.Errorf("could not find hash over entire envelope %v", dsseModel)
+	}
+	recordedEnvelopeHash, err := hex.DecodeString(*dsseModel.EnvelopeHash.Value)
+	if err != nil {
+		t.Errorf("error converting Envelope hash to []byte: %v", err)
+	}
+
+	if !bytes.Equal(envelopeHash[:], recordedEnvelopeHash) {
+		t.Fatal(fmt.Errorf("envelope hash %v doesnt match the payload we sent %v", hex.EncodeToString(envelopeHash[:]),
+			*dsseModel.EnvelopeHash.Value))
+	}
+
+	if len(dsseModel.Signatures) != 1 {
+		t.Fatalf("expected one signatures but got %d instead", len(dsseModel.Signatures))
+	}
+
+	out = util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "dsse", "--public-key", pubKeyPath)
+	util.OutputContains(t, out, "Entry already exists")
+}
+func TestDsseMultiSig(t *testing.T) {
+	td := t.TempDir()
+	attestationPath := filepath.Join(td, "attestation.json")
+	ecdsapubKeyPath := filepath.Join(td, "ecdsapub.pem")
+	rsapubKeyPath := filepath.Join(td, "rsapub.pem")
+
+	b, eb := GenerateDoubleSignedDSSE(t)
+
+	util.Write(t, string(eb), attestationPath)
+	util.Write(t, sigx509.ECDSAPub, ecdsapubKeyPath)
+	util.Write(t, sigx509.PubKey, rsapubKeyPath)
+
+	out := util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "dsse", "--public-key", ecdsapubKeyPath, "--public-key", rsapubKeyPath)
+	util.OutputContains(t, out, "Created entry at")
+	uuid := util.GetUUIDFromUploadOutput(t, out)
+
+	out = util.RunCli(t, "get", "--uuid", uuid, "--format=json")
+	g := util.GetOut{}
+	if err := json.Unmarshal([]byte(out), &g); err != nil {
+		t.Fatal(err)
+	}
+	// The attestation should not be stored
+
+	if len(g.Attestation) > 0 {
+		t.Fatal("unexpected attestation present in response")
+	}
+
+	payloadHash := sha256.Sum256(b)
+	envelopeHash := sha256.Sum256(eb)
+
+	dsseModel := &models.DSSEV001Schema{}
+	if err := types.DecodeEntry(g.Body.(map[string]interface{})["DSSEObj"], dsseModel); err != nil {
+		t.Errorf("could not convert body into dsse type: %v", err)
+	}
+	if dsseModel.PayloadHash == nil {
+		t.Errorf("could not find hash over payload %v", dsseModel)
+	}
+	recordedPayloadHash, err := hex.DecodeString(*dsseModel.PayloadHash.Value)
+	if err != nil {
+		t.Errorf("error converting payload hash to []byte: %v", err)
+	}
+
+	if !bytes.Equal(payloadHash[:], recordedPayloadHash) {
+		t.Fatal(fmt.Errorf("payload hash %v doesnt match the payload we sent %v", hex.EncodeToString(payloadHash[:]),
+			*dsseModel.PayloadHash.Value))
+	}
+
+	if dsseModel.EnvelopeHash == nil {
+		t.Errorf("could not find hash over envelope %v", dsseModel)
+	}
+	recordedEnvelopeHash, err := hex.DecodeString(*dsseModel.EnvelopeHash.Value)
+	if err != nil {
+		t.Errorf("error converting envelope hash to []byte: %v", err)
+	}
+
+	if !bytes.Equal(envelopeHash[:], recordedEnvelopeHash) {
+		t.Fatal(fmt.Errorf("envelope hash %v doesnt match the payload we sent %v", hex.EncodeToString(envelopeHash[:]),
+			*dsseModel.EnvelopeHash.Value))
+	}
+
+	if len(dsseModel.Signatures) != 2 {
+		t.Fatalf("expected two signatures but got %d instead", len(dsseModel.Signatures))
+	}
+
+	out = util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "dsse", "--public-key", ecdsapubKeyPath, "--public-key", rsapubKeyPath)
+	util.OutputContains(t, out, "Entry already exists")
+}
+
+func DecodeV001FromRekorResponse(t *testing.T, resp *entries.CreateLogEntryCreated) *V001Entry {
+	t.Helper()
+
+	for _, e := range resp.Payload {
+		b, err := base64.StdEncoding.DecodeString(e.Body.(string))
+		if err != nil {
+			t.Errorf("could not decode body into dsse type: %v", err)
+		}
+
+		pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer())
+		if err != nil {
+			t.Errorf("could not unmarshal body into dsse type: %v", err)
+		}
+		eimpl, err := types.UnmarshalEntry(pe)
+		if err != nil {
+			t.Errorf("could not unmarshal body into dsse v0.0.1 type: %v", err)
+		}
+		return eimpl.(*V001Entry)
+	}
+	return nil
+}
+
+// TestSendingCanonicalizedDSSE tests uploading a valid canonicalized entry. This should fail because the type requires
+// the ProposedContent fields to be submitted on upload, and canonicalized entries will not have those fields persisted.
+func TestSendingCanonicalizedDSSE(t *testing.T) {
+	b, eb := GenerateSingleSignedDSSE(t)
+	sha := sha256.New()
+	sha.Write(b)
+	payloadHashBytes := sha.Sum(nil)
+	payloadHashStr := hex.EncodeToString(payloadHashBytes)
+	sha.Reset()
+	sha.Write(eb)
+	envelopeHashBytes := sha.Sum(nil)
+	envelopeHashStr := hex.EncodeToString(envelopeHashBytes)
+
+	ap := types.ArtifactProperties{
+		ArtifactBytes:  eb,
+		PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub)},
+	}
+
+	ei := NewEntry()
+
+	entry, err := ei.CreateFromArtifactProperties(context.Background(), ap)
+	if err != nil {
+		t.Fatalf("error creating entry: %v", err)
+	}
+
+	dsse_entry := entry.(*models.DSSE)
+	v001 := dsse_entry.Spec.(models.DSSEV001Schema)
+	v001.EnvelopeHash = &models.DSSEV001SchemaEnvelopeHash{
+		Algorithm: swag.String("sha256"),
+		Value:     swag.String(envelopeHashStr),
+	}
+	v001.PayloadHash = &models.DSSEV001SchemaPayloadHash{
+		Algorithm: swag.String("sha256"),
+		Value:     swag.String(payloadHashStr),
+	}
+	env := &dsse.Envelope{}
+	if err := json.Unmarshal([]byte(*v001.ProposedContent.Envelope), env); err != nil {
+		t.Fatalf("error extracting DSSE envelope")
+	}
+	pk := v001.ProposedContent.Verifiers[0]
+	v001.Signatures = []*models.DSSEV001SchemaSignaturesItems0{
+		{
+			Signature: &env.Signatures[0].Sig,
+			Verifier:  &pk,
+		},
+	}
+	// erase proposed content and overwrite previous
+	v001.ProposedContent = nil
+	dsse_entry.Spec = v001
+
+	rc, err := client.GetRekorClient(rekorServer())
+	if err != nil {
+		t.Errorf("error getting client: %v", err)
+	}
+
+	params := &entries.CreateLogEntryParams{}
+	params.SetProposedEntry(dsse_entry)
+	params.SetContext(context.Background())
+	params.SetTimeout(5 * time.Second)
+
+	if _, err = rc.Entries.CreateLogEntry(params); err == nil {
+		t.Fatalf("expected error submitting canonicalized entry to rekor")
+	}
+	e, ok := err.(*entries.CreateLogEntryBadRequest)
+	if !ok {
+		t.Errorf("unexpected error returned from rekor: %v", err.Error())
+	}
+	if !strings.Contains(e.Payload.Message, "missing proposed content") {
+		t.Errorf("expected error message to include 'missing proposed content': %v", e.Payload.Message)
+	}
+}
+
+// TestSendingEntryWithClientComputedHashes tests uploading a valid entry with incorrect client-computed digests
+// over the entire envelope and payload. The hashes should be computed server side, and the request should be rejected
+func TestSendingEntryWithClientComputedHashes(t *testing.T) {
+	b, eb := GenerateSingleSignedDSSE(t)
+	sha := sha256.New()
+	sha.Write(b)
+	payloadHashBytes := sha.Sum(nil)
+	payloadHashStr := hex.EncodeToString(payloadHashBytes)
+	t.Logf(payloadHashStr)
+	sha.Reset()
+	sha.Write(eb)
+	envelopeHashBytes := sha.Sum(nil)
+	envelopeHashStr := hex.EncodeToString(envelopeHashBytes)
+	t.Logf(envelopeHashStr)
+
+	ap := types.ArtifactProperties{
+		ArtifactBytes:  eb,
+		PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub)},
+	}
+
+	ei := NewEntry()
+
+	entry, err := ei.CreateFromArtifactProperties(context.Background(), ap)
+	if err != nil {
+		t.Fatalf("error creating entry: %v", err)
+	}
+
+	dsse_entry := entry.(*models.DSSE)
+	v001 := dsse_entry.Spec.(models.DSSEV001Schema)
+	v001.EnvelopeHash = &models.DSSEV001SchemaEnvelopeHash{
+		Algorithm: swag.String("sha256"),
+		Value:     swag.String("8810ad581e59f2bc3928b261707a71308f7e139eb04820366dc4d5c18d980225"),
+	}
+	v001.PayloadHash = &models.DSSEV001SchemaPayloadHash{
+		Algorithm: swag.String("sha256"),
+		Value:     swag.String("8810ad581e59f2bc3928b261707a71308f7e139eb04820366dc4d5c18d980225"),
+	}
+	dsse_entry.Spec = v001
+
+	rc, err := client.GetRekorClient(rekorServer())
+	if err != nil {
+		t.Errorf("error getting client: %v", err)
+	}
+
+	params := &entries.CreateLogEntryParams{}
+	params.SetProposedEntry(dsse_entry)
+	params.SetContext(context.Background())
+	params.SetTimeout(5 * time.Second)
+
+	_, err = rc.Entries.CreateLogEntry(params)
+	if err == nil {
+		t.Error("expected error uploading bad entry to Rekor")
+	}
+
+	e, ok := err.(*entries.CreateLogEntryBadRequest)
+	if !ok {
+		t.Errorf("unexpected error returned from rekor: %v", err.Error())
+	}
+	if !strings.Contains(e.Payload.Message, "either proposedContent or envelopeHash, payloadHash, and signatures must be present but not both") {
+		t.Errorf("unexpected error message returned: %v", e.Payload.Message)
+	}
+}
+
+// TestMismatchedKeySingleSigner tests uploading a valid entry with the incorrect public key; this should be rejected by Rekor
+func TestMismatchedKeySingleSigner(t *testing.T) {
+	_, eb := GenerateSingleSignedDSSE(t)
+
+	ap := types.ArtifactProperties{
+		ArtifactBytes:  eb,
+		PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub)}, // this is the matching key, we will swap it out momentarily
+	}
+
+	ei := NewEntry()
+
+	entry, err := ei.CreateFromArtifactProperties(context.Background(), ap)
+	if err != nil {
+		t.Fatalf("error creating entry: %v", err)
+	}
+
+	// swap out good public key for mismatched one
+	dsse_entry := entry.(*models.DSSE)
+	v001 := dsse_entry.Spec.(models.DSSEV001Schema)
+	v001.ProposedContent.Verifiers[0] = strfmt.Base64(sigx509.RSACert)
+	dsse_entry.Spec = v001
+
+	rc, err := client.GetRekorClient(rekorServer())
+	if err != nil {
+		t.Errorf("error getting client: %v", err)
+	}
+
+	params := &entries.CreateLogEntryParams{}
+	params.SetProposedEntry(dsse_entry)
+	params.SetContext(context.Background())
+	params.SetTimeout(5 * time.Second)
+
+	if _, err = rc.Entries.CreateLogEntry(params); err == nil {
+		t.Fatalf("expected error submitting canonicalized entry to rekor")
+	}
+	e, ok := err.(*entries.CreateLogEntryBadRequest)
+	if !ok {
+		t.Errorf("unexpected error returned from rekor: %v", err.Error())
+	}
+	if !strings.Contains(e.Payload.Message, "could not verify envelope") {
+		t.Errorf("expected error message to include 'could not verify envelope': %v", e.Payload.Message)
+	}
+}
+
+// TestNoSignature tests sending a valid JSON object as the DSSE envelope, but one that omits the
+// signature. This should not be accepted by Rekor.
+func TestNoSignature(t *testing.T) {
+	_, eb := GenerateSingleSignedDSSE(t)
+
+	ap := types.ArtifactProperties{
+		ArtifactBytes:  eb,
+		PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub)}, //, []byte(sigx509.ECDSAPub)},
+	}
+
+	ei := NewEntry()
+
+	entry, err := ei.CreateFromArtifactProperties(context.Background(), ap)
+	if err != nil {
+		t.Fatalf("error creating entry: %v", err)
+	}
+
+	dsse_entry := entry.(*models.DSSE)
+	v001 := dsse_entry.Spec.(models.DSSEV001Schema)
+
+	env := &dsse.Envelope{}
+	if err := json.Unmarshal([]byte(*v001.ProposedContent.Envelope), env); err != nil {
+		t.Fatalf("error extracting DSSE envelope")
+	}
+
+	// remove signature
+	env.Signatures = []dsse.Signature{}
+
+	noSigEnv, err := json.Marshal(env)
+	if err != nil {
+		t.Fatalf("error marshalling sorted DSSE envelope")
+	}
+
+	v001.ProposedContent.Envelope = swag.String(string(noSigEnv))
+	dsse_entry.Spec = v001
+
+	rc, err := client.GetRekorClient(rekorServer())
+	if err != nil {
+		t.Errorf("error getting client: %v", err)
+	}
+
+	params := &entries.CreateLogEntryParams{}
+	params.SetProposedEntry(dsse_entry)
+	params.SetContext(context.Background())
+	params.SetTimeout(5 * time.Second)
+
+	if _, err := rc.Entries.CreateLogEntry(params); err == nil {
+		t.Errorf("expected error to be returned from rekor given lack of signature in envelope")
+	}
+}
+
+// TestTwoPublicKeysOneSignature tests uploading a valid entry with the both the correct and an incorrect public key;
+// this should be accepted by Rekor, but with only the key that successfully verifies the signature.
+func TestTwoPublicKeysOneSignature(t *testing.T) {
+	_, eb := GenerateSingleSignedDSSE(t)
+
+	ap := types.ArtifactProperties{
+		ArtifactBytes:  eb,
+		PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub), []byte(sigx509.ECDSAPub)},
+	}
+
+	ei := NewEntry()
+
+	entry, err := ei.CreateFromArtifactProperties(context.Background(), ap)
+	if err != nil {
+		t.Fatalf("error creating entry: %v", err)
+	}
+
+	rc, err := client.GetRekorClient(rekorServer())
+	if err != nil {
+		t.Errorf("error getting client: %v", err)
+	}
+
+	params := &entries.CreateLogEntryParams{}
+	params.SetProposedEntry(entry)
+	params.SetContext(context.Background())
+	params.SetTimeout(5 * time.Second)
+
+	resp, err := rc.Entries.CreateLogEntry(params)
+	if err != nil {
+		t.Errorf("unexpected error returned from rekor: %v", err.Error())
+	}
+
+	v001 := DecodeV001FromRekorResponse(t, resp)
+
+	if len(v001.DSSEObj.Signatures) != 1 {
+		t.Errorf("incorrect number of signatures returned in response: expected 1, got %d", len(v001.DSSEObj.Signatures))
+	}
+}
+
+// TestTwoPublicKeysTwoSignatures tests uploading a valid entry with the both the correct and an incorrect public key;
+// this should be rejected by Rekor, as both signatures were not successfully verified
+func TestTwoPublicKeysTwoSignatures(t *testing.T) {
+	_, eb := GenerateDoubleSignedDSSE(t)
+
+	ap := types.ArtifactProperties{
+		ArtifactBytes:  eb,
+		PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub), []byte(sigx509.RSACert)}, // missing RSA pub key
+	}
+
+	ei := NewEntry()
+
+	entry, err := ei.CreateFromArtifactProperties(context.Background(), ap)
+	if err != nil {
+		t.Fatalf("error creating entry: %v", err)
+	}
+
+	// swap out one of the good public keys for a mismatched one
+	dsse_entry := entry.(*models.DSSE)
+	v001 := dsse_entry.Spec.(models.DSSEV001Schema)
+	v001.ProposedContent.Verifiers[0] = strfmt.Base64(sigx509.RSACert)
+	v001.ProposedContent.Verifiers[1] = strfmt.Base64(sigx509.RSACert)
+	dsse_entry.Spec = v001
+
+	rc, err := client.GetRekorClient(rekorServer())
+	if err != nil {
+		t.Errorf("error getting client: %v", err)
+	}
+
+	params := &entries.CreateLogEntryParams{}
+	params.SetProposedEntry(dsse_entry)
+	params.SetContext(context.Background())
+	params.SetTimeout(5 * time.Second)
+
+	if _, err := rc.Entries.CreateLogEntry(params); err == nil {
+		t.Errorf("expected error to be returned from rekor given incorrect public keys provided")
+	}
+}
+
+// TestThreePublicKeysTwoSignatures tests uploading a valid entry with the both the correct keys and an additional
+// incorrect public key; this should be accepted by Rekor, but with only the two keys that successfully verified the signatures
+func TestThreePublicKeysTwoSignatures(t *testing.T) {
+	_, eb := GenerateDoubleSignedDSSE(t)
+
+	ap := types.ArtifactProperties{
+		ArtifactBytes:  eb,
+		PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub), []byte(sigx509.ECDSAPub), []byte(sigx509.RSACert)},
+	}
+
+	ei := NewEntry()
+
+	entry, err := ei.CreateFromArtifactProperties(context.Background(), ap)
+	if err != nil {
+		t.Fatalf("error creating entry: %v", err)
+	}
+
+	rc, err := client.GetRekorClient(rekorServer())
+	if err != nil {
+		t.Errorf("error getting client: %v", err)
+	}
+
+	params := &entries.CreateLogEntryParams{}
+	params.SetProposedEntry(entry)
+	params.SetContext(context.Background())
+	params.SetTimeout(5 * time.Second)
+
+	resp, err := rc.Entries.CreateLogEntry(params)
+	if err != nil {
+		t.Errorf("unexpected error returned from rekor: %v", err.Error())
+	}
+
+	for _, k := range resp.Payload {
+		b, err := base64.StdEncoding.DecodeString(k.Body.(string))
+		if err != nil {
+			t.Errorf("unexpected error returned from rekor: %v", err.Error())
+		}
+
+		pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer())
+		if err != nil {
+			t.Errorf("unexpected error returned from rekor: %v", err.Error())
+		}
+		eimpl, err := types.UnmarshalEntry(pe)
+		if err != nil {
+			t.Errorf("unexpected error returned from rekor: %v", err.Error())
+		}
+
+		dsse_eimpl := eimpl.(*V001Entry)
+
+		if len(dsse_eimpl.DSSEObj.Signatures) != 2 {
+			t.Errorf("incorrect number of signatures returned in response: expected 2, got %d", len(dsse_eimpl.DSSEObj.Signatures))
+		}
+	}
+}
diff --git a/pkg/types/dsse/v0.0.1/entry_test.go b/pkg/types/dsse/v0.0.1/entry_test.go
new file mode 100644
index 0000000..789f795
--- /dev/null
+++ b/pkg/types/dsse/v0.0.1/entry_test.go
@@ -0,0 +1,509 @@
+//
+// Copyright 2023 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 dsse
+
+import (
+	"bytes"
+	"context"
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"encoding/pem"
+	"fmt"
+	"math/big"
+	"reflect"
+	"sort"
+	"strings"
+	"testing"
+
+	"github.com/go-openapi/runtime"
+	"github.com/go-openapi/strfmt"
+	"github.com/go-openapi/swag"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/in-toto/in-toto-golang/in_toto"
+	slsaCommon "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
+	slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
+	"github.com/secure-systems-lab/go-securesystemslib/dsse"
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/types"
+	"github.com/sigstore/sigstore/pkg/signature"
+	sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse"
+	"go.uber.org/goleak"
+)
+
+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 envelope(t *testing.T, k *ecdsa.PrivateKey, payload []byte) *dsse.Envelope {
+
+	s, err := signature.LoadECDSASigner(k, crypto.SHA256)
+	if err != nil {
+		t.Fatal(err)
+	}
+	signer, err := dsse.NewEnvelopeSigner(&sigdsse.SignerAdapter{
+		SignatureSigner: s,
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	dsseEnv, err := signer.SignPayload(context.Background(), "application/vnd.in-toto+json", payload)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	return dsseEnv
+}
+
+func multiSignEnvelope(t *testing.T, k []*ecdsa.PrivateKey, payload []byte) *dsse.Envelope {
+	evps := []*sigdsse.SignerAdapter{}
+	for _, key := range k {
+		s, err := signature.LoadECDSASigner(key, crypto.SHA256)
+		if err != nil {
+			t.Fatal(err)
+		}
+		evps = append(evps, &sigdsse.SignerAdapter{
+			SignatureSigner: s,
+		})
+	}
+
+	signer, err := dsse.NewMultiEnvelopeSigner(2, evps[0], evps[1])
+	if err != nil {
+		t.Fatal(err)
+	}
+	dsseEnv, err := signer.SignPayload(context.Background(), in_toto.PayloadType, payload)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	return dsseEnv
+}
+
+func createRekorEnvelope(dsseEnv *dsse.Envelope, pub [][]byte) *models.DSSEV001SchemaProposedContent {
+
+	envelopeBytes, _ := json.Marshal(dsseEnv)
+	proposedContent := &models.DSSEV001SchemaProposedContent{
+		Envelope: swag.String(string(envelopeBytes)),
+	}
+	for _, key := range pub {
+		proposedContent.Verifiers = append(proposedContent.Verifiers, strfmt.Base64(key))
+	}
+	return proposedContent
+}
+
+func TestV001Entry_Unmarshal(t *testing.T) {
+	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)
+	}
+	pub := pem.EncodeToMemory(&pem.Block{
+		Bytes: der,
+		Type:  "PUBLIC KEY",
+	})
+
+	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	ca := &x509.Certificate{
+		SerialNumber: big.NewInt(1),
+	}
+	caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &priv.PublicKey, priv)
+	if err != nil {
+		t.Fatal(err)
+	}
+	pemBytes := pem.EncodeToMemory(&pem.Block{
+		Type:  "CERTIFICATE",
+		Bytes: caBytes,
+	})
+
+	invalid := dsse.Envelope{
+		Payload: "hello",
+		Signatures: []dsse.Signature{
+			{
+				Sig: string(strfmt.Base64("foobar")),
+			},
+		},
+	}
+
+	validPayload := "hellothispayloadisvalid"
+
+	tests := []struct {
+		env     *dsse.Envelope
+		name    string
+		it      *models.DSSEV001Schema
+		wantErr bool
+	}{
+		{
+			name:    "empty",
+			it:      &models.DSSEV001Schema{},
+			wantErr: true,
+		},
+		{
+			name: "missing envelope",
+			it: &models.DSSEV001Schema{
+				ProposedContent: &models.DSSEV001SchemaProposedContent{},
+			},
+			wantErr: true,
+		},
+		{
+			env:  envelope(t, key, []byte(validPayload)),
+			name: "valid",
+			it: &models.DSSEV001Schema{
+				ProposedContent: createRekorEnvelope(envelope(t, key, []byte(validPayload)), [][]byte{pub}),
+			},
+			wantErr: false,
+		},
+		{
+			env:  envelope(t, priv, []byte(validPayload)),
+			name: "cert",
+			it: &models.DSSEV001Schema{
+				ProposedContent: createRekorEnvelope(envelope(t, priv, []byte(validPayload)), [][]byte{pemBytes}),
+			},
+			wantErr: false,
+		},
+		{
+			env:  &invalid,
+			name: "invalid",
+			it: &models.DSSEV001Schema{
+				ProposedContent: createRekorEnvelope(&invalid, [][]byte{pub}),
+			},
+			wantErr: true,
+		},
+		{
+			env:  envelope(t, key, []byte(validPayload)),
+			name: "invalid key",
+			it: &models.DSSEV001Schema{
+				ProposedContent: createRekorEnvelope(envelope(t, key, []byte(validPayload)), [][]byte{[]byte("notavalidkey")}),
+			},
+			wantErr: true,
+		},
+		{
+			env:  multiSignEnvelope(t, []*ecdsa.PrivateKey{key, priv}, []byte(validPayload)),
+			name: "multi-key",
+			it: &models.DSSEV001Schema{
+				ProposedContent: createRekorEnvelope(multiSignEnvelope(t, []*ecdsa.PrivateKey{key, priv}, []byte(validPayload)), [][]byte{pub, pemBytes}),
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := &V001Entry{}
+
+			it := &models.DSSE{
+				Spec: tt.it,
+			}
+
+			var uv = func() error {
+				if err := v.Unmarshal(it); err != nil {
+					return err
+				}
+
+				if !tt.wantErr {
+					if ok, err := v.Insertable(); !ok || err != nil {
+						return fmt.Errorf("unexpected error calling Insertable: %w", err)
+					}
+				}
+				want := []string{}
+				for _, sig := range v.DSSEObj.Signatures {
+					keyHash := sha256.Sum256(*sig.Verifier)
+					want = append(want, "sha256:"+hex.EncodeToString(keyHash[:]))
+				}
+				decodedPayload, err := base64.StdEncoding.DecodeString(tt.env.Payload)
+				if err != nil {
+					return fmt.Errorf("could not decode envelope payload: %w", err)
+				}
+				h := sha256.Sum256(decodedPayload)
+				want = append(want, "sha256:"+hex.EncodeToString(h[:]))
+
+				envHashBytes := sha256.Sum256([]byte(*tt.it.ProposedContent.Envelope))
+				envHash := hex.EncodeToString(envHashBytes[:])
+
+				hashkey := strings.ToLower(fmt.Sprintf("sha256:%s", envHash))
+				want = append(want, hashkey)
+				got, _ := v.IndexKeys()
+				sort.Strings(got)
+				sort.Strings(want)
+				if !reflect.DeepEqual(got, want) {
+					t.Errorf("V001Entry.IndexKeys() = %v, want %v", got, want)
+				}
+				payloadBytes, _ := v.env.DecodeB64Payload()
+				payloadSha := sha256.Sum256(payloadBytes)
+				payloadHash := hex.EncodeToString(payloadSha[:])
+
+				canonicalBytes, err := v.Canonicalize(context.Background())
+				if err != nil {
+					t.Errorf("error canonicalizing entry: %v", err)
+				}
+
+				pe, err := models.UnmarshalProposedEntry(bytes.NewReader(canonicalBytes), runtime.JSONConsumer())
+				if err != nil {
+					t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tt.name, err)
+				}
+				canonicalEntry, err := types.UnmarshalEntry(pe)
+				if err != nil {
+					t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tt.name, err)
+				}
+				canonicalV001 := canonicalEntry.(*V001Entry)
+				if ok, err := canonicalV001.Insertable(); ok || err == nil {
+					t.Errorf("unexpected success testing Insertable against entry created from canonicalized content")
+				}
+				if *canonicalV001.DSSEObj.EnvelopeHash.Value != envHash {
+					t.Errorf("envelope hashes do not match post canonicalization: %v %v", *canonicalV001.DSSEObj.EnvelopeHash.Value, envHash)
+				}
+				if *canonicalV001.DSSEObj.PayloadHash.Value != payloadHash {
+					t.Errorf("payload hashes do not match post canonicalization: %v %v", canonicalV001.DSSEObj.PayloadHash.Value, payloadHash)
+				}
+				canonicalIndexKeys, _ := canonicalV001.IndexKeys()
+				if !cmp.Equal(got, canonicalIndexKeys, cmpopts.SortSlices(func(x, y string) bool { return x < y })) {
+					t.Errorf("index keys from hydrated object do not match those generated from canonicalized (and re-hydrated) object: %v %v", got, canonicalIndexKeys)
+				}
+
+				return nil
+			}
+			if err := uv(); (err != nil) != tt.wantErr {
+				t.Errorf("V001Entry.Unmarshal() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestV001Entry_IndexKeys(t *testing.T) {
+	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)
+	}
+	pub := pem.EncodeToMemory(&pem.Block{
+		Bytes: der,
+		Type:  "PUBLIC KEY",
+	})
+
+	tests := []struct {
+		name      string
+		statement in_toto.Statement
+		want      []string
+	}{
+		{
+			name: "standard",
+			want: []string{},
+			statement: in_toto.Statement{
+				Predicate: "hello",
+			},
+		},
+		{
+			name: "subject",
+			want: []string{"sha256:foo"},
+			statement: in_toto.Statement{
+				StatementHeader: in_toto.StatementHeader{
+					Subject: []in_toto.Subject{
+						{
+							Name: "foo",
+							Digest: map[string]string{
+								"sha256": "foo",
+							},
+						},
+					},
+				},
+				Predicate: "hello",
+			},
+		},
+		{
+			name: "slsa",
+			want: []string{"sha256:bar"},
+			statement: in_toto.Statement{
+				Predicate: slsa.ProvenancePredicate{
+					Materials: []slsaCommon.ProvenanceMaterial{
+						{
+							URI: "foo",
+							Digest: map[string]string{
+								"sha256": "bar",
+							}},
+					},
+				},
+			},
+		},
+		{
+			name: "slsa wit header",
+			want: []string{"sha256:foo", "sha256:bar"},
+			statement: in_toto.Statement{
+				StatementHeader: in_toto.StatementHeader{
+					Subject: []in_toto.Subject{
+						{
+							Name: "foo",
+							Digest: map[string]string{
+								"sha256": "foo",
+							},
+						},
+					},
+				},
+				Predicate: slsa.ProvenancePredicate{
+					Materials: []slsaCommon.ProvenanceMaterial{
+						{
+							URI: "foo",
+							Digest: map[string]string{
+								"sha256": "bar",
+							}},
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			b, err := json.Marshal(tt.statement)
+			if err != nil {
+				t.Fatal(err)
+			}
+			pe := &models.DSSE{
+				APIVersion: swag.String(APIVERSION),
+				Spec: &models.DSSEV001Schema{
+					ProposedContent: createRekorEnvelope(envelope(t, key, b), [][]byte{pub}),
+				},
+			}
+			v := V001Entry{}
+			if err := v.Unmarshal(pe); err != nil {
+				t.Error(err)
+			}
+			want := []string{}
+			for _, sig := range v.DSSEObj.Signatures {
+				keyHash := sha256.Sum256(*sig.Verifier)
+				want = append(want, "sha256:"+hex.EncodeToString(keyHash[:]))
+			}
+			decodedPayload, _ := base64.StdEncoding.DecodeString(v.env.Payload)
+			h := sha256.Sum256(decodedPayload)
+			want = append(want, "sha256:"+hex.EncodeToString(h[:]))
+
+			envHashBytes := sha256.Sum256([]byte(*v.DSSEObj.ProposedContent.Envelope))
+			envHash := hex.EncodeToString(envHashBytes[:])
+
+			hashkey := strings.ToLower(fmt.Sprintf("sha256:%s", envHash))
+			want = append(want, hashkey)
+			want = append(want, tt.want...)
+			got, err := v.IndexKeys()
+			if err != nil {
+				t.Error(err)
+			}
+			sort.Strings(got)
+			sort.Strings(want)
+			if !cmp.Equal(got, want, cmpopts.EquateEmpty()) {
+				t.Errorf("V001Entry.IndexKeys() = %v, want %v", got, want)
+			}
+		})
+	}
+}
+
+func TestInsertable(t *testing.T) {
+	type TestCase struct {
+		caseDesc      string
+		entry         V001Entry
+		expectSuccess bool
+	}
+
+	testCases := []TestCase{
+		{
+			caseDesc: "valid entry",
+			entry: V001Entry{
+				DSSEObj: models.DSSEV001Schema{
+					ProposedContent: &models.DSSEV001SchemaProposedContent{
+						Envelope: swag.String("envelope"),
+						Verifiers: []strfmt.Base64{
+							[]byte("keys"),
+						},
+					},
+				},
+			},
+			expectSuccess: true,
+		},
+		{
+			caseDesc: "missing public keys",
+			entry: V001Entry{
+				DSSEObj: models.DSSEV001Schema{
+					ProposedContent: &models.DSSEV001SchemaProposedContent{
+						Envelope: swag.String("envelope"),
+						/*
+							Verifiers: []strfmt.Base64{
+								[]byte("keys"),
+							},
+						*/
+					},
+				},
+			},
+			expectSuccess: false,
+		},
+		{
+			caseDesc: "missing envelope",
+			entry: V001Entry{
+				DSSEObj: models.DSSEV001Schema{
+					ProposedContent: &models.DSSEV001SchemaProposedContent{
+						//Envelope: swag.String("envelope"),
+						Verifiers: []strfmt.Base64{
+							[]byte("keys"),
+						},
+					},
+				},
+			},
+			expectSuccess: false,
+		},
+		{
+			caseDesc: "missing proposed content obj",
+			entry: V001Entry{
+				DSSEObj: models.DSSEV001Schema{
+					/*
+						ProposedContent: &models.DSSEV001SchemaProposedContent{
+							Envelope: swag.String("envelope"),
+							Verifiers: []strfmt.Base64{
+								[]byte("keys"),
+							},
+						},
+					*/
+				},
+			},
+			expectSuccess: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.caseDesc, func(t *testing.T) {
+			if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess {
+				t.Errorf("unexpected result calling Insertable: %v", err)
+			}
+		})
+	}
+}
diff --git a/pkg/types/dsse/v0.0.1/fuzz_test.go b/pkg/types/dsse/v0.0.1/fuzz_test.go
new file mode 100644
index 0000000..3715995
--- /dev/null
+++ b/pkg/types/dsse/v0.0.1/fuzz_test.go
@@ -0,0 +1,95 @@
+//
+// Copyright 2023 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 dsse
+
+import (
+	"context"
+	"sync"
+	"testing"
+
+	fuzz "github.com/AdamKorcz/go-fuzz-headers-1"
+	"github.com/go-openapi/swag"
+
+	fuzzUtils "github.com/sigstore/rekor/pkg/fuzz"
+	"github.com/sigstore/rekor/pkg/generated/models"
+	"github.com/sigstore/rekor/pkg/types"
+	"github.com/sigstore/rekor/pkg/types/dsse"
+)
+
+var initter sync.Once
+
+func FuzzDSSECreateProposedEntry(f *testing.F) {
+	f.Fuzz(func(t *testing.T, propsData []byte) {
+		initter.Do(fuzzUtils.SetFuzzLogger)
+
+		ff := fuzz.NewConsumer(propsData)
+
+		props, cleanup, err := fuzzUtils.CreateProps(ff)
+		if err != nil {
+			t.Skip()
+		}
+		defer cleanup()
+
+		it := dsse.New()
+		entry, err := it.CreateProposedEntry(context.Background(), APIVERSION, props)
+		if err != nil {
+			t.Skip()
+		}
+
+		ei, err := types.CreateVersionedEntry(entry)
+		if err != nil {
+			t.Skip()
+		}
+
+		if ok, err := ei.Insertable(); !ok || err != nil {
+			t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err)
+		}
+
+		if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil {
+			t.Errorf("valid insertable entry should be able to be canonicalized: %v", err)
+		}
+
+		_, _ = ei.IndexKeys()
+	})
+}
+
+func FuzzDSSEUnmarshalAndCanonicalize(f *testing.F) {
+	f.Fuzz(func(t *testing.T, entryData []byte) {
+		initter.Do(fuzzUtils.SetFuzzLogger)
+
+		ff := fuzz.NewConsumer(entryData)
+
+		targetV001 := &models.DSSEV001Schema{}
+
+		if err := ff.GenerateStruct(targetV001); err != nil {
+			t.Skip()
+		}
+
+		targetEntry := &models.DSSE{
+			APIVersion: swag.String(APIVERSION),
+			Spec:       targetV001,
+		}
+
+		ei, err := types.UnmarshalEntry(targetEntry)
+		if err != nil {
+			t.Skip()
+		}
+
+		if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil {
+			t.Errorf("error canonicalizing unmarshalled entry: %v", err)
+		}
+	})
+}
diff --git a/pkg/types/intoto/e2e_test.go b/pkg/types/intoto/e2e_test.go
index 597d2fb..2bb4130 100644
--- a/pkg/types/intoto/e2e_test.go
+++ b/pkg/types/intoto/e2e_test.go
@@ -394,3 +394,35 @@ func TestValidationSearchIntotoV002MissingPayloadHash(t *testing.T) {
 		t.Fatalf("expected status 400, got %d instead: %v", resp.StatusCode, string(c))
 	}
 }
+
+func TestValidationSearchIntotoV001MissingPayloadHash(t *testing.T) {
+	body := `{
+		"entries":[
+			{
+				"kind":"intoto",
+				"apiVersion": "0.0.1",
+				"spec":{
+					"content":{
+						"hash":{
+							"algorithm":"sha256",
+							"value":"ebbfddda6277af199e93c5bb5cf5998a79311de238e49bcc8ac24102698761bb"
+						}
+					},
+					"publicKey":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNwRENDQWlxZ0F3SUJBZ0lVYWhsOEFRd1lZV05ZbnV6dlFvOEVrN1dMTURvd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpJd09ESTJNREV4TnpFM1doY05Nakl3T0RJMk1ERXlOekUzV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVLWmZEQzlpalVyclpBWE9jWFYrQXFHRUlTSlEzVHRqSndJdEEKdTE3Rml2aWpnSk1hYUhGNDcrT3Z2OVR1ekFDQ3lpSUV5UDUyZXI2ZmF5bmZKYVVqOEtPQ0FVa3dnZ0ZGTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVHQldUCkMwdUU3dTRQcURVRjFYV0c0QlVWVUpBd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0pRWURWUjBSQVFIL0JCc3dHWUVYYzJGemIyRnJhWEpoTmpFeE5FQm5iV0ZwYkM1amIyMHdLUVlLS3dZQgpCQUdEdnpBQkFRUWJhSFIwY0hNNkx5OWhZMk52ZFc1MGN5NW5iMjluYkdVdVkyOXRNSUdMQmdvckJnRUVBZFo1CkFnUUNCSDBFZXdCNUFIY0FDR0NTOENoUy8yaEYwZEZySjRTY1JXY1lyQlk5d3pqU2JlYThJZ1kyYjNJQUFBR0MKMTdtSmhnQUFCQU1BU0RCR0FpRUFoS09BSkdWVlhCb1cxTDR4alk5eWJWOGZUUXN5TStvUEpIeDk5S29LYUpVQwpJUURCZDllc1Q0Mk1STng3Vm9BM1paKzV4akhNZWR6amVxQ2ZoZTcvd1pxYTlUQUtCZ2dxaGtqT1BRUURBd05vCkFEQmxBakVBcnBkeXlFRjc3b2JyTENMUXpzYmIxM2lsNjd3dzM4Y050amdNQml6Y2VUakRiY2VLeVFSN1RKNHMKZENsclkxY1BBakE4aXB6SUQ4VU1CaGxkSmUvZXJGcGdtN2swNWFic2lPN3V5dVZuS29VNk0rTXJ6VVUrZTlGdwpJRGhCanVRa1dRYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
+				}
+			}
+		]
+	}
+	`
+	resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()),
+		"application/json",
+		bytes.NewBuffer([]byte(body)))
+	if err != nil {
+		t.Fatal(err)
+	}
+	c, _ := ioutil.ReadAll(resp.Body)
+	// No failure when payload hash is not present for canonicalization
+	if resp.StatusCode != 200 {
+		t.Fatalf("expected status 200, got %d instead: %v", resp.StatusCode, string(c))
+	}
+}
diff --git a/pkg/types/intoto/v0.0.1/entry_test.go b/pkg/types/intoto/v0.0.1/entry_test.go
index d48b2b4..9a6b2ee 100644
--- a/pkg/types/intoto/v0.0.1/entry_test.go
+++ b/pkg/types/intoto/v0.0.1/entry_test.go
@@ -362,6 +362,43 @@ func TestV001Entry_Unmarshal(t *testing.T) {
 	}
 }
 
+// Demonstrates that Unmarshal and Canonicalize will succeed with only a hash,
+// since committed entries will have no envelope and may have no payload hash
+func TestV001EntryWithoutEnvelopeOrPayloadHash(t *testing.T) {
+	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)
+	}
+	pub := pem.EncodeToMemory(&pem.Block{
+		Bytes: der,
+		Type:  "PUBLIC KEY",
+	})
+	m := &models.IntotoV001Schema{
+		PublicKey: p(pub),
+		Content: &models.IntotoV001SchemaContent{
+			Hash: &models.IntotoV001SchemaContentHash{
+				Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256),
+				Value:     swag.String("1a1707bb54e5fb4deddd19f07adcb4f1e022ca7879e3c8348da8d4fa496ae8e2"),
+			},
+		},
+	}
+	v := &V001Entry{}
+	it := &models.Intoto{
+		Spec: m,
+	}
+	if err := v.Unmarshal(it); err != nil {
+		t.Fatalf("error umarshalling intoto without envelope: %v", err)
+	}
+	_, err = v.Canonicalize(context.TODO())
+	if err != nil {
+		t.Fatalf("error canonicalizing intoto without envelope: %v", err)
+	}
+}
+
 func TestV001Entry_IndexKeys(t *testing.T) {
 	h := sha256.Sum256([]byte("foo"))
 	dataSHA := hex.EncodeToString(h[:])
diff --git a/tests/oss_fuzz.sh b/tests/oss_fuzz.sh
index 9369838..ff4b814 100755
--- a/tests/oss_fuzz.sh
+++ b/tests/oss_fuzz.sh
@@ -60,6 +60,8 @@ compile_native_go_fuzzer github.com/sigstore/rekor/pkg/types/helm/v0.0.1 FuzzHel
 compile_native_go_fuzzer github.com/sigstore/rekor/pkg/types/helm/v0.0.1 FuzzHelmProvenanceUnmarshal FuzzHelmProvenanceUnmarshal
 compile_native_go_fuzzer github.com/sigstore/rekor/pkg/types/rekord/v0.0.1 FuzzRekordCreateProposedEntry FuzzRekordCreateProposedEntry
 compile_native_go_fuzzer github.com/sigstore/rekor/pkg/types/rekord/v0.0.1 FuzzRekordUnmarshalAndCanonicalize FuzzRekordUnmarshalAndCanonicalize
+compile_native_go_fuzzer github.com/sigstore/rekor/pkg/types/dsse/v0.0.1 FuzzDSSECreateProposedEntry FuzzDSSECreateProposedEntry
+compile_native_go_fuzzer github.com/sigstore/rekor/pkg/types/dsse/v0.0.1 FuzzDSSEUnmarshalAndCanonicalize FuzzDSSEUnmarshalAndCanonicalize
 
 # Test 3rd party API that rekor/pkg/types/jar/v0.0.1 uses
 go mod edit -replace github.com/sassoftware/relic=$SRC/relic