github.com/danielqsj/helm@v2.0.0-alpha.4.0.20160908204436-976e0ba5199b+incompatible/pkg/provenance/sign_test.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package provenance 17 18 import ( 19 "io/ioutil" 20 "os" 21 "strings" 22 "testing" 23 24 pgperrors "golang.org/x/crypto/openpgp/errors" 25 ) 26 27 const ( 28 // testKeyFile is the secret key. 29 // Generating keys should be done with `gpg --gen-key`. The current key 30 // was generated to match Go's defaults (RSA/RSA 2048). It has no pass 31 // phrase. Use `gpg --export-secret-keys helm-test` to export the secret. 32 testKeyfile = "testdata/helm-test-key.secret" 33 34 // testPubfile is the public key file. 35 // Use `gpg --export helm-test` to export the public key. 36 testPubfile = "testdata/helm-test-key.pub" 37 38 // Generated name for the PGP key in testKeyFile. 39 testKeyName = `Helm Testing (This key should only be used for testing. DO NOT TRUST.) <helm-testing@helm.sh>` 40 41 testChartfile = "testdata/hashtest-1.2.3.tgz" 42 43 // testSigBlock points to a signature generated by an external tool. 44 // This file was generated with GnuPG: 45 // gpg --clearsign -u helm-test --openpgp testdata/msgblock.yaml 46 testSigBlock = "testdata/msgblock.yaml.asc" 47 48 // testTamperedSigBlock is a tampered copy of msgblock.yaml.asc 49 testTamperedSigBlock = "testdata/msgblock.yaml.tampered" 50 51 // testSumfile points to a SHA256 sum generated by an external tool. 52 // We always want to validate against an external tool's representation to 53 // verify that we haven't done something stupid. This file was generated 54 // with shasum. 55 // shasum -a 256 hashtest-1.2.3.tgz > testdata/hashtest.sha256 56 testSumfile = "testdata/hashtest.sha256" 57 ) 58 59 // testMessageBlock represents the expected message block for the testdata/hashtest chart. 60 const testMessageBlock = `description: Test chart versioning 61 name: hashtest 62 version: 1.2.3 63 64 ... 65 files: 66 hashtest-1.2.3.tgz: sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75 67 ` 68 69 func TestMessageBlock(t *testing.T) { 70 out, err := messageBlock(testChartfile) 71 if err != nil { 72 t.Fatal(err) 73 } 74 got := out.String() 75 76 if got != testMessageBlock { 77 t.Errorf("Expected:\n%q\nGot\n%q\n", testMessageBlock, got) 78 } 79 } 80 81 func TestParseMessageBlock(t *testing.T) { 82 md, sc, err := parseMessageBlock([]byte(testMessageBlock)) 83 if err != nil { 84 t.Fatal(err) 85 } 86 87 if md.Name != "hashtest" { 88 t.Errorf("Expected name %q, got %q", "hashtest", md.Name) 89 } 90 91 if lsc := len(sc.Files); lsc != 1 { 92 t.Errorf("Expected 1 file, got %d", lsc) 93 } 94 95 if hash, ok := sc.Files["hashtest-1.2.3.tgz"]; !ok { 96 t.Errorf("hashtest file not found in Files") 97 } else if hash != "sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75" { 98 t.Errorf("Unexpected hash: %q", hash) 99 } 100 } 101 102 func TestLoadKey(t *testing.T) { 103 k, err := loadKey(testKeyfile) 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 if _, ok := k.Identities[testKeyName]; !ok { 109 t.Errorf("Expected to load a key for user %q", testKeyName) 110 } 111 } 112 113 func TestLoadKeyRing(t *testing.T) { 114 k, err := loadKeyRing(testPubfile) 115 if err != nil { 116 t.Fatal(err) 117 } 118 119 if len(k) > 1 { 120 t.Errorf("Expected 1, got %d", len(k)) 121 } 122 123 for _, e := range k { 124 if ii, ok := e.Identities[testKeyName]; !ok { 125 t.Errorf("Expected %s in %v", testKeyName, ii) 126 } 127 } 128 } 129 130 func TestNewFromFiles(t *testing.T) { 131 s, err := NewFromFiles(testKeyfile, testPubfile) 132 if err != nil { 133 t.Fatal(err) 134 } 135 136 if _, ok := s.Entity.Identities[testKeyName]; !ok { 137 t.Errorf("Expected to load a key for user %q", testKeyName) 138 } 139 } 140 141 func TestSumArchive(t *testing.T) { 142 hash, err := sumArchive(testChartfile) 143 if err != nil { 144 t.Fatal(err) 145 } 146 147 sig, err := readSumFile(testSumfile) 148 if err != nil { 149 t.Fatal(err) 150 } 151 152 if !strings.Contains(sig, hash) { 153 t.Errorf("Expected %s to be in %s", hash, sig) 154 } 155 } 156 157 func TestClearSign(t *testing.T) { 158 signer, err := NewFromFiles(testKeyfile, testPubfile) 159 if err != nil { 160 t.Fatal(err) 161 } 162 163 sig, err := signer.ClearSign(testChartfile) 164 if err != nil { 165 t.Fatal(err) 166 } 167 t.Logf("Sig:\n%s", sig) 168 169 if !strings.Contains(sig, testMessageBlock) { 170 t.Errorf("expected message block to be in sig: %s", sig) 171 } 172 } 173 174 func TestDecodeSignature(t *testing.T) { 175 // Unlike other tests, this does a round-trip test, ensuring that a signature 176 // generated by the library can also be verified by the library. 177 178 signer, err := NewFromFiles(testKeyfile, testPubfile) 179 if err != nil { 180 t.Fatal(err) 181 } 182 183 sig, err := signer.ClearSign(testChartfile) 184 if err != nil { 185 t.Fatal(err) 186 } 187 188 f, err := ioutil.TempFile("", "helm-test-sig-") 189 if err != nil { 190 t.Fatal(err) 191 } 192 193 tname := f.Name() 194 defer func() { 195 os.Remove(tname) 196 }() 197 f.WriteString(sig) 198 f.Close() 199 200 sig2, err := signer.decodeSignature(tname) 201 if err != nil { 202 t.Fatal(err) 203 } 204 205 by, err := signer.verifySignature(sig2) 206 if err != nil { 207 t.Fatal(err) 208 } 209 210 if _, ok := by.Identities[testKeyName]; !ok { 211 t.Errorf("Expected identity %q", testKeyName) 212 } 213 } 214 215 func TestVerify(t *testing.T) { 216 signer, err := NewFromFiles(testKeyfile, testPubfile) 217 if err != nil { 218 t.Fatal(err) 219 } 220 221 if ver, err := signer.Verify(testChartfile, testSigBlock); err != nil { 222 t.Errorf("Failed to pass verify. Err: %s", err) 223 } else if len(ver.FileHash) == 0 { 224 t.Error("Verification is missing hash.") 225 } else if ver.SignedBy == nil { 226 t.Error("No SignedBy field") 227 } 228 229 if _, err = signer.Verify(testChartfile, testTamperedSigBlock); err == nil { 230 t.Errorf("Expected %s to fail.", testTamperedSigBlock) 231 } 232 233 switch err.(type) { 234 case pgperrors.SignatureError: 235 t.Logf("Tampered sig block error: %s (%T)", err, err) 236 default: 237 t.Errorf("Expected invalid signature error, got %q (%T)", err, err) 238 } 239 } 240 241 // readSumFile reads a file containing a sum generated by the UNIX shasum tool. 242 func readSumFile(sumfile string) (string, error) { 243 data, err := ioutil.ReadFile(sumfile) 244 if err != nil { 245 return "", err 246 } 247 248 sig := string(data) 249 parts := strings.SplitN(sig, " ", 2) 250 return parts[0], nil 251 }