diff --git a/glide.lock b/glide.lock
index 1bf1d6940f292cf22b858cc78949e04d9f7a596b..808be278c545d409e7fe8714a5713296a6827c6d 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,5 +1,5 @@
-hash: 410e784360a10f716d4bf4d22decf81f75b327d051b3f2d23f55aa9049c09676
-updated: 2016-08-19T12:19:48.074620307-06:00
+hash: 05c56f2ae4c8bcbaf2c428e2e070ec00f865b284ea61dd671e2c4e117f2d6528
+updated: 2016-08-19T17:30:32.462379907-06:00
 imports:
 - name: github.com/aokoli/goutils
   version: 9c37978a95bd5c709a15883b6242714ea6709e64
@@ -247,6 +247,17 @@ imports:
   subpackages:
   - codec
   - codec/codecgen
+- name: golang.org/x/crypto
+  version: c84e1f8e3a7e322d497cd16c0e8a13c7e127baf3
+  subpackages:
+  - cast5
+  - openpgp
+  - openpgp/armor
+  - openpgp/clearsign
+  - openpgp/elgamal
+  - openpgp/errors
+  - openpgp/packet
+  - openpgp/s2k
 - name: golang.org/x/net
   version: fb93926129b8ec0056f2f458b1f519654814edf0
   subpackages:
diff --git a/glide.yaml b/glide.yaml
index ed96b020413cefb30a05aa1a3ab4e18263e06bbd..2f7567469f7182c385f4f9dc39381ebc0f24ce35 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -50,3 +50,6 @@ import:
 - package: google.golang.org/cloud
   vcs: git
   repo: https://code.googlesource.com/gocloud
+- package: golang.org/x/crypto
+  subpackages:
+  - openpgp
diff --git a/pkg/provenance/doc.go b/pkg/provenance/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..dacfa9e6920e72557971fb9609f6a61d54161e79
--- /dev/null
+++ b/pkg/provenance/doc.go
@@ -0,0 +1,37 @@
+/*
+Copyright 2016 The Kubernetes Authors All rights reserved.
+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 provenance provides tools for establishing the authenticity of a chart.
+
+In Helm, provenance is established via several factors. The primary factor is the
+cryptographic signature of a chart. Chart authors may sign charts, which in turn
+provide the necessary metadata to ensure the integrity of the chart file, the
+Chart.yaml, and the referenced Docker images.
+
+A provenance file is clear-signed. This provides cryptographic verification that
+a particular block of information (Chart.yaml, archive file, images) have not
+been tampered with or altered. To learn more, read the GnuPG documentation on
+clear signatures:
+https://www.gnupg.org/gph/en/manual/x135.html
+
+The cryptography used by Helm should be compatible with OpenGPG. For example,
+you should be able to verify a signature by importing the desired public key
+and using `gpg --verify`, `keybase pgp verify`, or similar:
+
+	$  gpg --verify some.sig
+	gpg: Signature made Mon Jul 25 17:23:44 2016 MDT using RSA key ID 1FC18762
+	gpg: Good signature from "Helm Testing (This key should only be used for testing. DO NOT TRUST.) <helm-testing@helm.sh>" [ultimate]
+*/
+package provenance // import "k8s.io/helm/pkg/provenance"
diff --git a/pkg/provenance/sign.go b/pkg/provenance/sign.go
new file mode 100644
index 0000000000000000000000000000000000000000..3dd8cfb74026f49b1ec18e21a8ed5322bc7f3b18
--- /dev/null
+++ b/pkg/provenance/sign.go
@@ -0,0 +1,280 @@
+/*
+Copyright 2016 The Kubernetes Authors All rights reserved.
+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 provenance
+
+import (
+	"bytes"
+	"crypto"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+
+	"github.com/ghodss/yaml"
+
+	"golang.org/x/crypto/openpgp"
+	"golang.org/x/crypto/openpgp/clearsign"
+	"golang.org/x/crypto/openpgp/packet"
+
+	"k8s.io/helm/pkg/chartutil"
+	hapi "k8s.io/helm/pkg/proto/hapi/chart"
+)
+
+var defaultPGPConfig = packet.Config{
+	DefaultHash: crypto.SHA512,
+}
+
+// SumCollection represents a collecton of file and image checksums.
+//
+// Files are of the form:
+//	FILENAME: "sha256:SUM"
+// Images are of the form:
+//	"IMAGE:TAG": "sha256:SUM"
+// Docker optionally supports sha512, and if this is the case, the hash marker
+// will be 'sha512' instead of 'sha256'.
+type SumCollection struct {
+	Files  map[string]string `json:"files"`
+	Images map[string]string `json:"images,omitempty"`
+}
+
+// Signatory signs things.
+//
+// Signatories can be constructed from a PGP private key file using NewFromFiles
+// or they can be constructed manually by setting the Entity to a valid
+// PGP entity.
+//
+// The same Signatory can be used to sign or validate multiple charts.
+type Signatory struct {
+	// The signatory for this instance of Helm. This is used for signing.
+	Entity *openpgp.Entity
+	// The keyring for this instance of Helm. This is used for verification.
+	KeyRing openpgp.EntityList
+}
+
+// NewFromFiles constructs a new Signatory from the PGP key in the given filename.
+//
+// This will emit an error if it cannot find a valid GPG keyfile (entity) at the
+// given location.
+//
+// Note that the keyfile may have just a public key, just a private key, or
+// both. The Signatory methods may have different requirements of the keys. For
+// example, ClearSign must have a valid `openpgp.Entity.PrivateKey` before it
+// can sign something.
+func NewFromFiles(keyfile, keyringfile string) (*Signatory, error) {
+	e, err := loadKey(keyfile)
+	if err != nil {
+		return nil, err
+	}
+
+	ring, err := loadKeyRing(keyringfile)
+	if err != nil {
+		return nil, err
+	}
+
+	return &Signatory{
+		Entity:  e,
+		KeyRing: ring,
+	}, nil
+}
+
+// Sign signs a chart with the given key.
+//
+// This takes the path to a chart archive file and a key, and it returns a clear signature.
+//
+// The Signatory must have a valid Entity.PrivateKey for this to work. If it does
+// not, an error will be returned.
+func (s *Signatory) ClearSign(chartpath string) (string, error) {
+	if s.Entity.PrivateKey == nil {
+		return "", errors.New("private key not found")
+	}
+
+	if fi, err := os.Stat(chartpath); err != nil {
+		return "", err
+	} else if fi.IsDir() {
+		return "", errors.New("cannot sign a directory")
+	}
+
+	out := bytes.NewBuffer(nil)
+
+	b, err := messageBlock(chartpath)
+	if err != nil {
+		return "", nil
+	}
+
+	// Sign the buffer
+	w, err := clearsign.Encode(out, s.Entity.PrivateKey, &defaultPGPConfig)
+	if err != nil {
+		return "", err
+	}
+	_, err = io.Copy(w, b)
+	w.Close()
+	return out.String(), err
+}
+
+func (s *Signatory) Verify(chartpath, sigpath string) (bool, error) {
+	for _, fname := range []string{chartpath, sigpath} {
+		if fi, err := os.Stat(fname); err != nil {
+			return false, err
+		} else if fi.IsDir() {
+			return false, fmt.Errorf("%s cannot be a directory", fname)
+		}
+	}
+
+	// First verify the signature
+	sig, err := s.decodeSignature(sigpath)
+	if err != nil {
+		return false, fmt.Errorf("failed to decode signature: %s", err)
+	}
+
+	by, err := s.verifySignature(sig)
+	if err != nil {
+		return false, err
+	}
+	for n := range by.Identities {
+		log.Printf("info: %s signed by %q", sigpath, n)
+	}
+
+	// Second, verify the hash of the tarball.
+
+	return true, nil
+}
+
+func (s *Signatory) decodeSignature(filename string) (*clearsign.Block, error) {
+	data, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	block, _ := clearsign.Decode(data)
+	if block == nil {
+		// There was no sig in the file.
+		return nil, errors.New("signature block not found")
+	}
+
+	return block, nil
+}
+
+// verifySignature verifies that the given block is validly signed, and returns the signer.
+func (s *Signatory) verifySignature(block *clearsign.Block) (*openpgp.Entity, error) {
+	return openpgp.CheckDetachedSignature(
+		s.KeyRing,
+		bytes.NewBuffer(block.Bytes),
+		block.ArmoredSignature.Body,
+	)
+}
+
+func messageBlock(chartpath string) (*bytes.Buffer, error) {
+	var b *bytes.Buffer
+	// Checksum the archive
+	chash, err := sumArchive(chartpath)
+	if err != nil {
+		return b, err
+	}
+
+	base := filepath.Base(chartpath)
+	sums := &SumCollection{
+		Files: map[string]string{
+			base: "sha256:" + chash,
+		},
+	}
+
+	// Load the archive into memory.
+	chart, err := chartutil.LoadFile(chartpath)
+	if err != nil {
+		return b, err
+	}
+
+	// Buffer a hash + checksums YAML file
+	data, err := yaml.Marshal(chart.Metadata)
+	if err != nil {
+		return b, err
+	}
+
+	// FIXME: YAML uses ---\n as a file start indicator, but this is not legal in a PGP
+	// clearsign block. So we use ...\n, which is the YAML document end marker.
+	// http://yaml.org/spec/1.2/spec.html#id2800168
+	b = bytes.NewBuffer(data)
+	b.WriteString("\n...\n")
+
+	data, err = yaml.Marshal(sums)
+	if err != nil {
+		return b, err
+	}
+	b.Write(data)
+
+	return b, nil
+}
+
+// parseMessageBlock
+func parseMessageBlock(data []byte) (*hapi.Metadata, *SumCollection, error) {
+	// This sucks.
+	parts := bytes.Split(data, []byte("\n...\n"))
+	if len(parts) < 2 {
+		return nil, nil, errors.New("message block must have at least two parts")
+	}
+
+	md := &hapi.Metadata{}
+	sc := &SumCollection{}
+
+	if err := yaml.Unmarshal(parts[0], md); err != nil {
+		return md, sc, err
+	}
+	err := yaml.Unmarshal(parts[1], sc)
+	return md, sc, err
+}
+
+// loadKey loads a GPG key found at a particular path.
+func loadKey(keypath string) (*openpgp.Entity, error) {
+	f, err := os.Open(keypath)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	pr := packet.NewReader(f)
+	return openpgp.ReadEntity(pr)
+}
+
+func loadKeyRing(ringpath string) (openpgp.EntityList, error) {
+	f, err := os.Open(ringpath)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	return openpgp.ReadKeyRing(f)
+}
+
+// sumArchive calculates a SHA256 hash (like Docker) for a given file.
+//
+// It takes the path to the archive file, and returns a string representation of
+// the SHA256 sum.
+//
+// The intended use of this function is to generate a sum of a chart TGZ file.
+func sumArchive(filename string) (string, error) {
+	f, err := os.Open(filename)
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+
+	hash := crypto.SHA256.New()
+	io.Copy(hash, f)
+	return hex.EncodeToString(hash.Sum(nil)), nil
+}
diff --git a/pkg/provenance/sign_test.go b/pkg/provenance/sign_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5dfd6bd68467786d5af2f5a3c5e4b7d1b42cf5aa
--- /dev/null
+++ b/pkg/provenance/sign_test.go
@@ -0,0 +1,249 @@
+/*
+Copyright 2016 The Kubernetes Authors All rights reserved.
+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 provenance
+
+import (
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+
+	pgperrors "golang.org/x/crypto/openpgp/errors"
+)
+
+const (
+	// testKeyFile is the secret key.
+	// Generating keys should be done with `gpg --gen-key`. The current key
+	// was generated to match Go's defaults (RSA/RSA 2048). It has no pass
+	// phrase. Use `gpg --export-secret-keys helm-test` to export the secret.
+	testKeyfile = "testdata/helm-test-key.secret"
+
+	// testPubfile is the public key file.
+	// Use `gpg --export helm-test` to export the public key.
+	testPubfile = "testdata/helm-test-key.pub"
+
+	// Generated name for the PGP key in testKeyFile.
+	testKeyName = `Helm Testing (This key should only be used for testing. DO NOT TRUST.) <helm-testing@helm.sh>`
+
+	testChartfile = "testdata/hashtest-1.2.3.tgz"
+
+	// testSigBlock points to a signature generated by an external tool.
+	// This file was generated with GnuPG:
+	// gpg --clearsign -u helm-test --openpgp testdata/msgblock.yaml
+	testSigBlock = "testdata/msgblock.yaml.asc"
+
+	// testTamperedSigBlock is a tampered copy of msgblock.yaml.asc
+	testTamperedSigBlock = "testdata/msgblock.yaml.tampered"
+
+	// testSumfile points to a SHA256 sum generated by an external tool.
+	// We always want to validate against an external tool's representation to
+	// verify that we haven't done something stupid. This file was generated
+	// with shasum.
+	// shasum -a 256 hashtest-1.2.3.tgz > testdata/hashtest.sha256
+	testSumfile = "testdata/hashtest.sha256"
+)
+
+// testMessageBlock represents the expected message block for the testdata/hashtest chart.
+const testMessageBlock = `description: Test chart versioning
+name: hashtest
+version: 1.2.3
+
+...
+files:
+  hashtest-1.2.3.tgz: sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75
+`
+
+func TestMessageBlock(t *testing.T) {
+	out, err := messageBlock(testChartfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+	got := out.String()
+
+	if got != testMessageBlock {
+		t.Errorf("Expected:\n%q\nGot\n%q\n", testMessageBlock, got)
+	}
+}
+
+func TestParseMessageBlock(t *testing.T) {
+	md, sc, err := parseMessageBlock([]byte(testMessageBlock))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if md.Name != "hashtest" {
+		t.Errorf("Expected name %q, got %q", "hashtest", md.Name)
+	}
+
+	if lsc := len(sc.Files); lsc != 1 {
+		t.Errorf("Expected 1 file, got %d", lsc)
+	}
+
+	if hash, ok := sc.Files["hashtest-1.2.3.tgz"]; !ok {
+		t.Errorf("hashtest file not found in Files")
+	} else if hash != "sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75" {
+		t.Errorf("Unexpected hash: %q", hash)
+	}
+}
+
+func TestLoadKey(t *testing.T) {
+	k, err := loadKey(testKeyfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, ok := k.Identities[testKeyName]; !ok {
+		t.Errorf("Expected to load a key for user %q", testKeyName)
+	}
+}
+
+func TestLoadKeyRing(t *testing.T) {
+	k, err := loadKeyRing(testPubfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(k) > 1 {
+		t.Errorf("Expected 1, got %d", len(k))
+	}
+
+	for _, e := range k {
+		if ii, ok := e.Identities[testKeyName]; !ok {
+			t.Errorf("Expected %s in %v", testKeyName, ii)
+		}
+	}
+}
+
+func TestNewFromFiles(t *testing.T) {
+	s, err := NewFromFiles(testKeyfile, testPubfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, ok := s.Entity.Identities[testKeyName]; !ok {
+		t.Errorf("Expected to load a key for user %q", testKeyName)
+	}
+}
+
+func TestSumArchive(t *testing.T) {
+	hash, err := sumArchive(testChartfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	sig, err := readSumFile(testSumfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !strings.Contains(sig, hash) {
+		t.Errorf("Expected %s to be in %s", hash, sig)
+	}
+}
+
+func TestClearSign(t *testing.T) {
+	signer, err := NewFromFiles(testKeyfile, testPubfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	sig, err := signer.ClearSign(testChartfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Logf("Sig:\n%s", sig)
+
+	if !strings.Contains(sig, testMessageBlock) {
+		t.Errorf("expected message block to be in sig: %s", sig)
+	}
+}
+
+func TestDecodeSignature(t *testing.T) {
+	// Unlike other tests, this does a round-trip test, ensuring that a signature
+	// generated by the library can also be verified by the library.
+
+	signer, err := NewFromFiles(testKeyfile, testPubfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	sig, err := signer.ClearSign(testChartfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	f, err := ioutil.TempFile("", "helm-test-sig-")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	tname := f.Name()
+	defer func() {
+		os.Remove(tname)
+	}()
+	f.WriteString(sig)
+	f.Close()
+
+	sig2, err := signer.decodeSignature(tname)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	by, err := signer.verifySignature(sig2)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, ok := by.Identities[testKeyName]; !ok {
+		t.Errorf("Expected identity %q", testKeyName)
+	}
+}
+
+func TestVerify(t *testing.T) {
+	signer, err := NewFromFiles(testKeyfile, testPubfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	passed, err := signer.Verify(testChartfile, testSigBlock)
+	if !passed {
+		t.Errorf("Failed to pass verify. Err: %s", err)
+	}
+
+	passed, err = signer.Verify(testChartfile, testTamperedSigBlock)
+	if passed {
+		t.Errorf("Expected %s to fail.", testTamperedSigBlock)
+	}
+
+	switch err.(type) {
+	case pgperrors.SignatureError:
+		t.Logf("Tampered sig block error: %s (%T)", err, err)
+	default:
+		t.Errorf("Expected invalid signature error, got %q (%T)", err, err)
+	}
+}
+
+// readSumFile reads a file containing a sum generated by the UNIX shasum tool.
+func readSumFile(sumfile string) (string, error) {
+	data, err := ioutil.ReadFile(sumfile)
+	if err != nil {
+		return "", err
+	}
+
+	sig := string(data)
+	parts := strings.SplitN(sig, " ", 2)
+	return parts[0], nil
+}
diff --git a/pkg/provenance/testdata/hashtest-1.2.3.tgz b/pkg/provenance/testdata/hashtest-1.2.3.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..1e89b524f4fc89e4a483ef8dc4fefdc79d64786f
Binary files /dev/null and b/pkg/provenance/testdata/hashtest-1.2.3.tgz differ
diff --git a/pkg/provenance/testdata/hashtest.sha256 b/pkg/provenance/testdata/hashtest.sha256
new file mode 100644
index 0000000000000000000000000000000000000000..829031f9d2fc97cc5c473af8c2614d176a32f7fc
--- /dev/null
+++ b/pkg/provenance/testdata/hashtest.sha256
@@ -0,0 +1 @@
+8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75  hashtest-1.2.3.tgz
diff --git a/pkg/provenance/testdata/hashtest/.helmignore b/pkg/provenance/testdata/hashtest/.helmignore
new file mode 100644
index 0000000000000000000000000000000000000000..435b756d88557faadb806d8df4130c39f1aa184d
--- /dev/null
+++ b/pkg/provenance/testdata/hashtest/.helmignore
@@ -0,0 +1,5 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+.git
diff --git a/pkg/provenance/testdata/hashtest/Chart.yaml b/pkg/provenance/testdata/hashtest/Chart.yaml
new file mode 100755
index 0000000000000000000000000000000000000000..342631ef88239eed8aaa1a31ef76f6e60f47810d
--- /dev/null
+++ b/pkg/provenance/testdata/hashtest/Chart.yaml
@@ -0,0 +1,3 @@
+description: Test chart versioning
+name: hashtest
+version: 1.2.3
diff --git a/pkg/provenance/testdata/hashtest/values.yaml b/pkg/provenance/testdata/hashtest/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..0827a01fbefaf7a54d847f4160c757a2d41f56f3
--- /dev/null
+++ b/pkg/provenance/testdata/hashtest/values.yaml
@@ -0,0 +1,4 @@
+# Default values for hashtest.
+# This is a YAML-formatted file.
+# Declare name/value pairs to be passed into your templates.
+# name: value
diff --git a/pkg/provenance/testdata/helm-test-key.pub b/pkg/provenance/testdata/helm-test-key.pub
new file mode 100644
index 0000000000000000000000000000000000000000..38714f25adaf701b08e11fd559a587074bbde0e4
Binary files /dev/null and b/pkg/provenance/testdata/helm-test-key.pub differ
diff --git a/pkg/provenance/testdata/helm-test-key.secret b/pkg/provenance/testdata/helm-test-key.secret
new file mode 100644
index 0000000000000000000000000000000000000000..a966aef93ed97d01d764f29940738df6df2d9d24
Binary files /dev/null and b/pkg/provenance/testdata/helm-test-key.secret differ
diff --git a/pkg/provenance/testdata/msgblock.yaml b/pkg/provenance/testdata/msgblock.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..0fdbda8ce50cf5c0ae2fb903fee96cf031cce5de
--- /dev/null
+++ b/pkg/provenance/testdata/msgblock.yaml
@@ -0,0 +1,7 @@
+description: Test chart versioning
+name: hashtest
+version: 1.2.3
+
+...
+files:
+  hashtest-1.2.3.tgz: sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75
diff --git a/pkg/provenance/testdata/msgblock.yaml.asc b/pkg/provenance/testdata/msgblock.yaml.asc
new file mode 100644
index 0000000000000000000000000000000000000000..5a34d6c52cb8891e16de986c5fdc27fff19ccd83
--- /dev/null
+++ b/pkg/provenance/testdata/msgblock.yaml.asc
@@ -0,0 +1,21 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+description: Test chart versioning
+name: hashtest
+version: 1.2.3
+
+...
+files:
+  hashtest-1.2.3.tgz: sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75
+-----BEGIN PGP SIGNATURE-----
+Comment: GPGTools - https://gpgtools.org
+
+iQEcBAEBCgAGBQJXlp8KAAoJEIQ7v5gfwYdiE7sIAJYDiza+asekeooSXLvQiK+G
+PKnveqQpx49EZ6L7Y7UlW25SyH8EjXXHeJysDywCXF3w4luxN9n56ffU0KEW11IY
+F+JSjmgIWLS6ti7ZAGEi6JInQ/30rOAIpTEBRBL2IueW3m63mezrGK6XkBlGqpor
+C9WKeqLi+DWlMoBtsEy3Uk0XP6pn/qBFICYAbLQQU0sCCUT8CBA8f8aidxi7aw9t
+i404yYF+Dvc6i4JlSG77SV0ZJBWllUvsWoCd9Jli0NAuaMqmE7mzcEt/dE+Fm2Ql
+Bx3tr1WS4xTRiFQdcOttOl93H+OaHTh+Y0qqLTzzpCvqmttG0HfI6lMeCs7LeyA=
+=vEK+
+-----END PGP SIGNATURE-----
diff --git a/pkg/provenance/testdata/msgblock.yaml.tampered b/pkg/provenance/testdata/msgblock.yaml.tampered
new file mode 100644
index 0000000000000000000000000000000000000000..f15811bb21ef15c9a6595d056f258af20d42cd71
--- /dev/null
+++ b/pkg/provenance/testdata/msgblock.yaml.tampered
@@ -0,0 +1,21 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+description: Test chart versioning
+name: hashtest
+version: 1.2.3+tampered
+
+...
+files:
+  hashtest-1.2.3.tgz: sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75
+-----BEGIN PGP SIGNATURE-----
+Comment: GPGTools - https://gpgtools.org
+
+iQEcBAEBCgAGBQJXlp8KAAoJEIQ7v5gfwYdiE7sIAJYDiza+asekeooSXLvQiK+G
+PKnveqQpx49EZ6L7Y7UlW25SyH8EjXXHeJysDywCXF3w4luxN9n56ffU0KEW11IY
+F+JSjmgIWLS6ti7ZAGEi6JInQ/30rOAIpTEBRBL2IueW3m63mezrGK6XkBlGqpor
+C9WKeqLi+DWlMoBtsEy3Uk0XP6pn/qBFICYAbLQQU0sCCUT8CBA8f8aidxi7aw9t
+i404yYF+Dvc6i4JlSG77SV0ZJBWllUvsWoCd9Jli0NAuaMqmE7mzcEt/dE+Fm2Ql
+Bx3tr1WS4xTRiFQdcOttOl93H+OaHTh+Y0qqLTzzpCvqmttG0HfI6lMeCs7LeyA=
+=vEK+
+-----END PGP SIGNATURE-----
diff --git a/pkg/provenance/testdata/regen-hashtest.sh b/pkg/provenance/testdata/regen-hashtest.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4381fd0b180408ea9c81a82420a381589c7da420
--- /dev/null
+++ b/pkg/provenance/testdata/regen-hashtest.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+helm package hashtest
+shasum -a 256 hashtest-1.2.3.tgz > hashtest.sha256