From 0977422d07af67d45319f2cd4538368a6c4732e7 Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Tue, 29 Nov 2022 20:23:21 -0600 Subject: [PATCH 1/6] Revamping rekor e2e - part 4 of N - Updated tests for ssh - Updated tests for minisign Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- pkg/pki/minisign/minisign_e2e_test.go | 121 ++++++++++++++++++ pkg/pki/ssh/e2e_test.go | 64 ++++++++++ tests/ssh.go => pkg/pki/ssh/ssh_e2e_test.go | 11 +- tests/e2e_test.go | 133 -------------------- 4 files changed, 189 insertions(+), 140 deletions(-) create mode 100644 pkg/pki/minisign/minisign_e2e_test.go create mode 100644 pkg/pki/ssh/e2e_test.go rename tests/ssh.go => pkg/pki/ssh/ssh_e2e_test.go (81%) diff --git a/pkg/pki/minisign/minisign_e2e_test.go b/pkg/pki/minisign/minisign_e2e_test.go new file mode 100644 index 000000000..d7a5e8a0a --- /dev/null +++ b/pkg/pki/minisign/minisign_e2e_test.go @@ -0,0 +1,121 @@ +// +// Copyright 2022 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 minisign + +import ( + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/sigstore/rekor/pkg/util" +) + +func TestMinisign(t *testing.T) { + // Create a keypair + keyPath := filepath.Join(t.TempDir(), "minisign.key") + pubPath := filepath.Join(t.TempDir(), "minisign.pub") + + // Set an empty password, we have to hit enter twice to confirm + util.Run(t, "\n\n", "minisign", "-G", "-s", keyPath, "-p", pubPath) + + // Create a random artifact and sign it. + artifactPath := filepath.Join(t.TempDir(), "artifact") + sigPath := filepath.Join(t.TempDir(), "signature.asc") + util.CreateArtifact(t, artifactPath) + + // Send in one empty password over stdin + out := util.Run(t, "\n", "minisign", "-S", "-s", keyPath, "-m", artifactPath, "-x", sigPath) + t.Log(out) + + // Now upload to the log! + out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, + "--public-key", pubPath, "--pki-format", "minisign") + util.OutputContains(t, out, "Created entry at") + + uuidA := util.GetUUIDFromUploadOutput(t, out) + + out = util.RunCli(t, "verify", "--artifact", artifactPath, "--signature", sigPath, + "--public-key", pubPath, "--pki-format", "minisign") + util.OutputContains(t, out, "Inclusion Proof") + + out = util.RunCli(t, "search", "--public-key", pubPath, "--pki-format", "minisign") + util.OutputContains(t, out, uuidA) + + // crease a second artifact and sign it + artifactPath_B := filepath.Join(t.TempDir(), "artifact2") + util.CreateArtifact(t, artifactPath_B) + out = util.Run(t, "\n", "minisign", "-S", "-s", keyPath, "-m", artifactPath_B, "-x", sigPath) + // Now upload to the log! + out = util.RunCli(t, "upload", "--artifact", artifactPath_B, "--signature", sigPath, + "--public-key", pubPath, "--pki-format", "minisign") + util.OutputContains(t, out, "Created entry at") + uuidB := util.GetUUIDFromUploadOutput(t, out) + + tests := []struct { + name string + expectedUuidACount int + expectedUuidBCount int + artifact string + operator string + }{ + { + name: "artifact A AND signature should return artifact A", + expectedUuidACount: 1, + expectedUuidBCount: 0, + artifact: artifactPath, + operator: "and", + }, + { + name: "artifact A OR signature should return artifact A and B", + expectedUuidACount: 1, + expectedUuidBCount: 1, + artifact: artifactPath, + operator: "or", + }, + { + name: "artifact B AND signature should return artifact B", + expectedUuidACount: 0, + expectedUuidBCount: 1, + artifact: artifactPath_B, + operator: "and", + }, + { + name: "artifact B OR signature should return artifact A and B", + expectedUuidACount: 1, + expectedUuidBCount: 1, + artifact: artifactPath_B, + operator: "or", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + out = util.RunCli(t, "search", "--public-key", pubPath, "--pki-format", "minisign", + "--operator", test.operator, "--artifact", test.artifact) + + expected := map[string]int{uuidA: test.expectedUuidACount, uuidB: test.expectedUuidBCount} + actual := map[string]int{ + uuidA: strings.Count(out, uuidA), + uuidB: strings.Count(out, uuidB), + } + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected to find %v, found %v", expected, actual) + } + }) + } +} diff --git a/pkg/pki/ssh/e2e_test.go b/pkg/pki/ssh/e2e_test.go new file mode 100644 index 000000000..11c387814 --- /dev/null +++ b/pkg/pki/ssh/e2e_test.go @@ -0,0 +1,64 @@ +// +// Copyright 2022 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 ssh + +import ( + "github.com/sigstore/rekor/pkg/util" + "io/ioutil" + "path/filepath" + "strings" + "testing" +) + +func TestSSH(t *testing.T) { + td := t.TempDir() + // Create a keypair + keyPath := filepath.Join(td, "id_rsa") + pubPath := filepath.Join(td, "id_rsa.pub") + + if err := ioutil.WriteFile(pubPath, []byte(publicKey), 0600); err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(keyPath, []byte(privateKey), 0600); err != nil { + t.Fatal(err) + } + + // Create a random artifact and sign it. + artifactPath := filepath.Join(td, "artifact") + sigPath := filepath.Join(td, "signature.sig") + artifact := util.CreateArtifact(t, artifactPath) + + sig := SSHSign(t, strings.NewReader(artifact)) + if err := ioutil.WriteFile(sigPath, []byte(sig), 0600); err != nil { + t.Fatal(err) + } + + // Now upload to the log! + out := util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, + "--public-key", pubPath, "--pki-format", "ssh") + util.OutputContains(t, out, "Created entry at") + + uuid := util.GetUUIDFromUploadOutput(t, out) + + out = util.RunCli(t, "verify", "--artifact", artifactPath, "--signature", sigPath, + "--public-key", pubPath, "--pki-format", "ssh") + util.OutputContains(t, out, "Inclusion Proof") + + out = util.RunCli(t, "search", "--public-key", pubPath, "--pki-format", "ssh") + util.OutputContains(t, out, uuid) +} diff --git a/tests/ssh.go b/pkg/pki/ssh/ssh_e2e_test.go similarity index 81% rename from tests/ssh.go rename to pkg/pki/ssh/ssh_e2e_test.go index a07cf32a0..3c9b3045d 100644 --- a/tests/ssh.go +++ b/pkg/pki/ssh/ssh_e2e_test.go @@ -14,22 +14,19 @@ // limitations under the License. //go:build e2e -// +build e2e -package e2e +package ssh import ( "bytes" "io" "io/ioutil" "testing" - - "github.com/sigstore/rekor/pkg/pki/ssh" ) var ( // Generated with "ssh-keygen -C test@rekor.dev -f id_rsa" - sshPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY----- + privateKey = `-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn NhAAAAAwEAAQAAAYEA16H5ImoRO7mr41r8Z8JFBdu6jIM+6XU8M0r9F81RuhLYqzr9zw1n LeGCqFxPXNBKm8ZyH2BCsBHsbXbwe85IMHM3SUh8X/9fI0Lpi5/xbqAproFUpNR+UJYv6s @@ -68,7 +65,7 @@ lL4qHtXBEzaT8okkcGZBHdSx3gk4TzCsEDOP7ZZPLq42lpKMK10zFPTMd0maXtJDYKU/b4 gAATvvPoylyYUAAAAOdGVzdEByZWtvci5kZXYBAgMEBQ== -----END OPENSSH PRIVATE KEY----- ` - sshPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDXofkiahE7uavjWvxnwkUF27qMgz7pdTwzSv0XzVG6EtirOv3PDWct4YKoXE9c0EqbxnIfYEKwEextdvB7zkgwczdJSHxf/18jQumLn/FuoCmugVSk1H5Qli/qzwBpaTnOk3WuakGuoYUl8ZAokKKgOKLA0aZJ1WRQ2ZCZggA3EkwNZiY17y9Q6HqdgQcH6XN8aAMADNVJdMAJb33hSRJjjsAPTmzBTishP8lYDoGRSsSE7/8XRBCEV5E4I8mI9GElcZwV/1KJx98mpH8QvMzXM1idFcwPRtt1NTAOshwgUU0Fu1x8lU5RQIa6ZKW36qNQLvLxy/BscC7B/mdLptoDs/ot9NimUXZcgCR1a2Q3o7Wi6jIgcgJcyV10Nba81ol4RdN4qPHnVZIzuo+dBkqwG3CMtB4Rj84+Qi+7zyU01hIPreoxQDXaayiGPBUUIiAlW9gsiuRWJzNnu3cvuWDLVfQIkjh7Wug58z+v2NOJ7IMdyERillhzDcvVHaq14+U= test@rekor.dev + publicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDXofkiahE7uavjWvxnwkUF27qMgz7pdTwzSv0XzVG6EtirOv3PDWct4YKoXE9c0EqbxnIfYEKwEextdvB7zkgwczdJSHxf/18jQumLn/FuoCmugVSk1H5Qli/qzwBpaTnOk3WuakGuoYUl8ZAokKKgOKLA0aZJ1WRQ2ZCZggA3EkwNZiY17y9Q6HqdgQcH6XN8aAMADNVJdMAJb33hSRJjjsAPTmzBTishP8lYDoGRSsSE7/8XRBCEV5E4I8mI9GElcZwV/1KJx98mpH8QvMzXM1idFcwPRtt1NTAOshwgUU0Fu1x8lU5RQIa6ZKW36qNQLvLxy/BscC7B/mdLptoDs/ot9NimUXZcgCR1a2Q3o7Wi6jIgcgJcyV10Nba81ol4RdN4qPHnVZIzuo+dBkqwG3CMtB4Rj84+Qi+7zyU01hIPreoxQDXaayiGPBUUIiAlW9gsiuRWJzNnu3cvuWDLVfQIkjh7Wug58z+v2NOJ7IMdyERillhzDcvVHaq14+U= test@rekor.dev ` ) @@ -78,7 +75,7 @@ func SSHSign(t *testing.T, m io.Reader) []byte { if err != nil { t.Fatal(err) } - sig, err := ssh.Sign(sshPrivateKey, bytes.NewReader(data)) + sig, err := Sign(privateKey, bytes.NewReader(data)) if err != nil { t.Fatal(err) } diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 5f8520e44..eaf613ed1 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -37,7 +37,6 @@ import ( "os" "os/exec" "path/filepath" - "reflect" "regexp" "strconv" "strings" @@ -238,138 +237,6 @@ func TestSearchNoEntriesRC1(t *testing.T) { runCliErr(t, "search", "--email", "noone@internetz.com") } -func TestMinisign(t *testing.T) { - // Create a keypair - keyPath := filepath.Join(t.TempDir(), "minisign.key") - pubPath := filepath.Join(t.TempDir(), "minisign.pub") - - // Set an empty password, we have to hit enter twice to confirm - run(t, "\n\n", "minisign", "-G", "-s", keyPath, "-p", pubPath) - - // Create a random artifact and sign it. - artifactPath := filepath.Join(t.TempDir(), "artifact") - sigPath := filepath.Join(t.TempDir(), "signature.asc") - createArtifact(t, artifactPath) - - // Send in one empty password over stdin - out := run(t, "\n", "minisign", "-S", "-s", keyPath, "-m", artifactPath, "-x", sigPath) - t.Log(out) - - // Now upload to the log! - out = runCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, - "--public-key", pubPath, "--pki-format", "minisign") - outputContains(t, out, "Created entry at") - - uuidA := getUUIDFromUploadOutput(t, out) - - out = runCli(t, "verify", "--artifact", artifactPath, "--signature", sigPath, - "--public-key", pubPath, "--pki-format", "minisign") - outputContains(t, out, "Inclusion Proof") - - out = runCli(t, "search", "--public-key", pubPath, "--pki-format", "minisign") - outputContains(t, out, uuidA) - - // crease a second artifact and sign it - artifactPath_B := filepath.Join(t.TempDir(), "artifact2") - createArtifact(t, artifactPath_B) - out = run(t, "\n", "minisign", "-S", "-s", keyPath, "-m", artifactPath_B, "-x", sigPath) - // Now upload to the log! - out = runCli(t, "upload", "--artifact", artifactPath_B, "--signature", sigPath, - "--public-key", pubPath, "--pki-format", "minisign") - outputContains(t, out, "Created entry at") - uuidB := getUUIDFromUploadOutput(t, out) - - tests := []struct { - name string - expectedUuidACount int - expectedUuidBCount int - artifact string - operator string - }{ - { - name: "artifact A AND signature should return artifact A", - expectedUuidACount: 1, - expectedUuidBCount: 0, - artifact: artifactPath, - operator: "and", - }, - { - name: "artifact A OR signature should return artifact A and B", - expectedUuidACount: 1, - expectedUuidBCount: 1, - artifact: artifactPath, - operator: "or", - }, - { - name: "artifact B AND signature should return artifact B", - expectedUuidACount: 0, - expectedUuidBCount: 1, - artifact: artifactPath_B, - operator: "and", - }, - { - name: "artifact B OR signature should return artifact A and B", - expectedUuidACount: 1, - expectedUuidBCount: 1, - artifact: artifactPath_B, - operator: "or", - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - out = runCli(t, "search", "--public-key", pubPath, "--pki-format", "minisign", - "--operator", test.operator, "--artifact", test.artifact) - - expected := map[string]int{uuidA: test.expectedUuidACount, uuidB: test.expectedUuidBCount} - actual := map[string]int{ - uuidA: strings.Count(out, uuidA), - uuidB: strings.Count(out, uuidB), - } - if !reflect.DeepEqual(expected, actual) { - t.Errorf("expected to find %v, found %v", expected, actual) - } - }) - } -} - -func TestSSH(t *testing.T) { - td := t.TempDir() - // Create a keypair - keyPath := filepath.Join(td, "id_rsa") - pubPath := filepath.Join(td, "id_rsa.pub") - - if err := ioutil.WriteFile(pubPath, []byte(sshPublicKey), 0600); err != nil { - t.Fatal(err) - } - if err := ioutil.WriteFile(keyPath, []byte(sshPrivateKey), 0600); err != nil { - t.Fatal(err) - } - - // Create a random artifact and sign it. - artifactPath := filepath.Join(td, "artifact") - sigPath := filepath.Join(td, "signature.sig") - artifact := createArtifact(t, artifactPath) - - sig := SSHSign(t, strings.NewReader(artifact)) - if err := ioutil.WriteFile(sigPath, []byte(sig), 0600); err != nil { - t.Fatal(err) - } - - // Now upload to the log! - out := runCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, - "--public-key", pubPath, "--pki-format", "ssh") - outputContains(t, out, "Created entry at") - - uuid := getUUIDFromUploadOutput(t, out) - - out = runCli(t, "verify", "--artifact", artifactPath, "--signature", sigPath, - "--public-key", pubPath, "--pki-format", "ssh") - outputContains(t, out, "Inclusion Proof") - - out = runCli(t, "search", "--public-key", pubPath, "--pki-format", "ssh") - outputContains(t, out, uuid) -} - func TestIntoto(t *testing.T) { td := t.TempDir() attestationPath := filepath.Join(td, "attestation.json") From fa1106c1ec1fbb6adc3476352984b7f7f8dbad38 Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Sat, 3 Dec 2022 20:02:41 -0600 Subject: [PATCH 2/6] Refactor e2e - intoto,rekord,rfc3161 - refactored intoto rekord rfc3161 Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- pkg/types/intoto/e2e_test.go | 279 ++++++++++++++++++ pkg/types/rekord/e2e_test.go | 28 +- pkg/types/rfc3161/e2e_test.go | 50 ++++ {tests => pkg/types/rfc3161/tests}/test.tsr | Bin pkg/types/rfc3161/v0.0.1/entry_test.go | 2 +- pkg/util/util.go | 152 ++++++++++ tests/e2e_test.go | 302 +------------------- 7 files changed, 513 insertions(+), 300 deletions(-) create mode 100644 pkg/types/intoto/e2e_test.go create mode 100644 pkg/types/rfc3161/e2e_test.go rename {tests => pkg/types/rfc3161/tests}/test.tsr (100%) diff --git a/pkg/types/intoto/e2e_test.go b/pkg/types/intoto/e2e_test.go new file mode 100644 index 000000000..88b7bb10c --- /dev/null +++ b/pkg/types/intoto/e2e_test.go @@ -0,0 +1,279 @@ +// +// Copyright 2022 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 intoto + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "encoding/json" + "encoding/pem" + "fmt" + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/in-toto/in-toto-golang/in_toto" + 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" + + sigx509 "github.com/sigstore/rekor/pkg/pki/x509" + "github.com/sigstore/rekor/pkg/util" +) + +func TestIntoto(t *testing.T) { + td := t.TempDir() + attestationPath := filepath.Join(td, "attestation.json") + pubKeyPath := filepath.Join(td, "pub.pem") + + // 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: slsa.DigestSet{ + "foo": "bar", + }, + }, + }, + }, + Predicate: slsa.ProvenancePredicate{ + Builder: slsa.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(in_toto.PayloadType, b) + if err != nil { + t.Fatal(err) + } + + eb, err := json.Marshal(env) + if err != nil { + t.Fatal(err) + } + + util.Write(t, string(eb), attestationPath) + util.Write(t, sigx509.ECDSAPub, pubKeyPath) + + out := util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--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 be stored at /var/run/attestations/sha256:digest + + got := in_toto.ProvenanceStatement{} + if err := json.Unmarshal([]byte(g.Attestation), &got); err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(it, got); diff != "" { + t.Errorf("diff: %s", diff) + } + + attHash := sha256.Sum256(b) + + intotoModel := &models.IntotoV002Schema{} + if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoModel); err != nil { + t.Errorf("could not convert body into intoto type: %v", err) + } + if intotoModel.Content == nil || intotoModel.Content.PayloadHash == nil { + t.Errorf("could not find hash over attestation %v", intotoModel) + } + recordedPayloadHash, err := hex.DecodeString(*intotoModel.Content.PayloadHash.Value) + if err != nil { + t.Errorf("error converting attestation hash to []byte: %v", err) + } + + if !bytes.Equal(attHash[:], recordedPayloadHash) { + t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]), + *intotoModel.Content.PayloadHash.Value)) + } + + out = util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) + util.OutputContains(t, out, "Entry already exists") +} +func TestIntotoMultiSig(t *testing.T) { + td := t.TempDir() + attestationPath := filepath.Join(td, "attestation.json") + ecdsapubKeyPath := filepath.Join(td, "ecdsapub.pem") + rsapubKeyPath := filepath.Join(td, "rsapub.pem") + + // 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: slsa.DigestSet{ + "foo": "bar", + }, + }, + }, + }, + Predicate: slsa.ProvenancePredicate{ + Builder: slsa.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(in_toto.PayloadType, b) + if err != nil { + t.Fatal(err) + } + + eb, err := json.Marshal(env) + if err != nil { + t.Fatal(err) + } + + 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", "intoto", "--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 be stored at /var/run/attestations/$uuid + + got := in_toto.ProvenanceStatement{} + if err := json.Unmarshal([]byte(g.Attestation), &got); err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(it, got); diff != "" { + t.Errorf("diff: %s", diff) + } + + attHash := sha256.Sum256([]byte(g.Attestation)) + + intotoV002Model := &models.IntotoV002Schema{} + if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoV002Model); err != nil { + t.Errorf("could not convert body into intoto type: %v", err) + } + if intotoV002Model.Content.Hash == nil { + t.Errorf("could not find hash over attestation %v", intotoV002Model) + } + recordedPayloadHash, err := hex.DecodeString(*intotoV002Model.Content.PayloadHash.Value) + if err != nil { + t.Errorf("error converting attestation hash to []byte: %v", err) + } + + if !bytes.Equal(attHash[:], recordedPayloadHash) { + t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]), + *intotoV002Model.Content.PayloadHash.Value)) + } + + out = util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", ecdsapubKeyPath, "--public-key", rsapubKeyPath) + util.OutputContains(t, out, "Entry already exists") +} diff --git a/pkg/types/rekord/e2e_test.go b/pkg/types/rekord/e2e_test.go index 4afceaa9f..0de7fd839 100644 --- a/pkg/types/rekord/e2e_test.go +++ b/pkg/types/rekord/e2e_test.go @@ -14,13 +14,13 @@ // limitations under the License. //go:build e2e -// +build e2e package rekord import ( "crypto/sha256" "encoding/hex" + "io/ioutil" "os" "path/filepath" "testing" @@ -59,3 +59,29 @@ func TestUploadVerifyHashedRekord(t *testing.T) { util.OutputContains(t, out, "Inclusion Proof:") util.OutputContains(t, out, "Checkpoint:") } +func TestUploadVerifyRekord(t *testing.T) { + // Create a random artifact and sign it. + artifactPath := filepath.Join(t.TempDir(), "artifact") + sigPath := filepath.Join(t.TempDir(), "signature.asc") + + util.CreatedPGPSignedArtifact(t, artifactPath, sigPath) + + // Write the public key to a file + pubPath := filepath.Join(t.TempDir(), "pubKey.asc") + if err := ioutil.WriteFile(pubPath, []byte(util.PubKey), 0644); err != nil { + t.Fatal(err) + } + + // Verify should fail initially + out := util.RunCliErr(t, "verify", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) + util.OutputContains(t, out, "entry in log cannot be located") + + // It should upload successfully. + out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) + util.OutputContains(t, out, "Created entry at") + + // Now we should be able to verify it. + out = util.RunCli(t, "verify", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) + util.OutputContains(t, out, "Inclusion Proof:") + util.OutputContains(t, out, "Checkpoint:") +} diff --git a/pkg/types/rfc3161/e2e_test.go b/pkg/types/rfc3161/e2e_test.go new file mode 100644 index 000000000..1265b5290 --- /dev/null +++ b/pkg/types/rfc3161/e2e_test.go @@ -0,0 +1,50 @@ +// +// Copyright 2022 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 rfc3161 + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io/ioutil" + "testing" + + "github.com/sigstore/rekor/pkg/util" +) + +func TestTimestampArtifact(t *testing.T) { + var out string + out = util.RunCli(t, "upload", "--type", "rfc3161", "--artifact", "tests/test.tsr") + util.OutputContains(t, out, "Created entry at") + uuid := util.GetUUIDFromUploadOutput(t, out) + + artifactBytes, err := ioutil.ReadFile("tests/test.tsr") + if err != nil { + t.Error(err) + } + sha := sha256.Sum256(artifactBytes) + + out = util.RunCli(t, "upload", "--type", "rfc3161", "--artifact", "tests/test.tsr") + util.OutputContains(t, out, "Entry already exists") + + out = util.RunCli(t, "search", "--artifact", "tests/test.tsr") + util.OutputContains(t, out, uuid) + + out = util.RunCli(t, "search", "--sha", fmt.Sprintf("sha256:%s", hex.EncodeToString(sha[:]))) + util.OutputContains(t, out, uuid) +} diff --git a/tests/test.tsr b/pkg/types/rfc3161/tests/test.tsr similarity index 100% rename from tests/test.tsr rename to pkg/types/rfc3161/tests/test.tsr diff --git a/pkg/types/rfc3161/v0.0.1/entry_test.go b/pkg/types/rfc3161/v0.0.1/entry_test.go index fe9485574..b3e4dac5c 100644 --- a/pkg/types/rfc3161/v0.0.1/entry_test.go +++ b/pkg/types/rfc3161/v0.0.1/entry_test.go @@ -58,7 +58,7 @@ func TestCrossFieldValidation(t *testing.T) { expectValidationErrorMessage string } - tsrBytes, _ := os.ReadFile("../../../../tests/test.tsr") + tsrBytes, _ := os.ReadFile("../tests/test.tsr") testServer := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/util/util.go b/pkg/util/util.go index 548f02d30..1661e382e 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -42,6 +42,14 @@ var ( keys openpgp.EntityList ) +type GetOut struct { + Attestation string + AttestationType string + Body interface{} + LogIndex int + IntegratedTime int64 +} + // This was generated with gpg --gen-key, and all defaults. // The email is "test@rekor.dev", and the name is Rekor Test. // It should only be used for test purposes. @@ -127,6 +135,129 @@ aBIJN0iaJaXVB+JFbzXT1DLhqCR1T37zZSKnLMSKtvIe9UOO6Jy4mgX6CDjPM/Vu T4CBiqLxHYsQ9n8dT95t+poqJ10PVFkehb+8kh05e3ENd4xpkkdTfIY= =CwjQ -----END PGP PRIVATE KEY BLOCK-----` +var PrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +=CwjQ +-----END PGP PRIVATE KEY BLOCK-----` + +var PubKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +WR6Fv7ySHTl7cQ13jGmSR1N8hg== +=Fen+ +-----END PGP PUBLIC KEY BLOCK-----` func init() { p := os.Getenv("REKORTMPDIR") @@ -307,3 +438,24 @@ func SignPGP(b []byte) ([]byte, error) { } return buf.Bytes(), nil } +func Write(t *testing.T, data string, path string) { + t.Helper() + if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil { + t.Fatal(err) + } +} + +// CreatedPGPSignedArtifact gets the test dir setup correctly with some random artifacts and keys. +func CreatedPGPSignedArtifact(t *testing.T, artifactPath, sigPath string) { + t.Helper() + artifact := CreateArtifact(t, artifactPath) + + // Sign it with our key and write that to a file + signature, err := SignPGP([]byte(artifact)) + if err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(sigPath, signature, 0644); err != nil { + t.Fatal(err) + } +} diff --git a/tests/e2e_test.go b/tests/e2e_test.go index eaf613ed1..34de448e2 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -14,7 +14,6 @@ // limitations under the License. //go:build e2e -// +build e2e package e2e @@ -23,14 +22,9 @@ import ( "bytes" "context" "crypto" - "crypto/ecdsa" - "crypto/rsa" "crypto/sha256" - "crypto/x509" - "encoding/base64" "encoding/hex" "encoding/json" - "encoding/pem" "fmt" "io/ioutil" "net/http" @@ -50,16 +44,12 @@ import ( "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" - 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" sigx509 "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/signer" - "github.com/sigstore/rekor/pkg/types" _ "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1" rekord "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1" "github.com/sigstore/rekor/pkg/util" @@ -130,33 +120,6 @@ func TestDuplicates(t *testing.T) { outputContains(t, out, "Created entry at") } -func TestUploadVerifyRekord(t *testing.T) { - // Create a random artifact and sign it. - artifactPath := filepath.Join(t.TempDir(), "artifact") - sigPath := filepath.Join(t.TempDir(), "signature.asc") - - createdPGPSignedArtifact(t, artifactPath, sigPath) - - // Write the public key to a file - pubPath := filepath.Join(t.TempDir(), "pubKey.asc") - if err := ioutil.WriteFile(pubPath, []byte(publicKey), 0644); err != nil { - t.Fatal(err) - } - - // Verify should fail initially - out := runCliErr(t, "verify", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) - outputContains(t, out, "entry in log cannot be located") - - // It should upload successfully. - out = runCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) - outputContains(t, out, "Created entry at") - - // Now we should be able to verify it. - out = runCli(t, "verify", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) - outputContains(t, out, "Inclusion Proof:") - outputContains(t, out, "Checkpoint:") -} - func TestLogInfo(t *testing.T) { // TODO: figure out some way to check the length, add something, and make sure the length increments! out := runCli(t, "loginfo") @@ -237,263 +200,6 @@ func TestSearchNoEntriesRC1(t *testing.T) { runCliErr(t, "search", "--email", "noone@internetz.com") } -func TestIntoto(t *testing.T) { - td := t.TempDir() - attestationPath := filepath.Join(td, "attestation.json") - pubKeyPath := filepath.Join(td, "pub.pem") - - // Get some random data so it's unique each run - d := 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: slsa.DigestSet{ - "foo": "bar", - }, - }, - }, - }, - Predicate: slsa.ProvenancePredicate{ - Builder: slsa.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(in_toto.PayloadType, b) - if err != nil { - t.Fatal(err) - } - - eb, err := json.Marshal(env) - if err != nil { - t.Fatal(err) - } - - write(t, string(eb), attestationPath) - write(t, sigx509.ECDSAPub, pubKeyPath) - - out := runCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) - outputContains(t, out, "Created entry at") - uuid := getUUIDFromUploadOutput(t, out) - - out = runCli(t, "get", "--uuid", uuid, "--format=json") - g := getOut{} - if err := json.Unmarshal([]byte(out), &g); err != nil { - t.Fatal(err) - } - // The attestation should be stored at /var/run/attestations/sha256:digest - - got := in_toto.ProvenanceStatement{} - if err := json.Unmarshal([]byte(g.Attestation), &got); err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(it, got); diff != "" { - t.Errorf("diff: %s", diff) - } - - attHash := sha256.Sum256(b) - - intotoModel := &models.IntotoV002Schema{} - if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoModel); err != nil { - t.Errorf("could not convert body into intoto type: %v", err) - } - if intotoModel.Content == nil || intotoModel.Content.PayloadHash == nil { - t.Errorf("could not find hash over attestation %v", intotoModel) - } - recordedPayloadHash, err := hex.DecodeString(*intotoModel.Content.PayloadHash.Value) - if err != nil { - t.Errorf("error converting attestation hash to []byte: %v", err) - } - - if !bytes.Equal(attHash[:], recordedPayloadHash) { - t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]), - *intotoModel.Content.PayloadHash.Value)) - } - - out = runCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) - outputContains(t, out, "Entry already exists") -} - -func TestIntotoMultiSig(t *testing.T) { - td := t.TempDir() - attestationPath := filepath.Join(td, "attestation.json") - ecdsapubKeyPath := filepath.Join(td, "ecdsapub.pem") - rsapubKeyPath := filepath.Join(td, "rsapub.pem") - - // Get some random data so it's unique each run - d := 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: slsa.DigestSet{ - "foo": "bar", - }, - }, - }, - }, - Predicate: slsa.ProvenancePredicate{ - Builder: slsa.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(in_toto.PayloadType, b) - if err != nil { - t.Fatal(err) - } - - eb, err := json.Marshal(env) - if err != nil { - t.Fatal(err) - } - - write(t, string(eb), attestationPath) - write(t, sigx509.ECDSAPub, ecdsapubKeyPath) - write(t, sigx509.PubKey, rsapubKeyPath) - - out := runCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", ecdsapubKeyPath, "--public-key", rsapubKeyPath) - outputContains(t, out, "Created entry at") - uuid := getUUIDFromUploadOutput(t, out) - - out = runCli(t, "get", "--uuid", uuid, "--format=json") - g := getOut{} - if err := json.Unmarshal([]byte(out), &g); err != nil { - t.Fatal(err) - } - // The attestation should be stored at /var/run/attestations/$uuid - - got := in_toto.ProvenanceStatement{} - if err := json.Unmarshal([]byte(g.Attestation), &got); err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(it, got); diff != "" { - t.Errorf("diff: %s", diff) - } - - attHash := sha256.Sum256([]byte(g.Attestation)) - - intotoV002Model := &models.IntotoV002Schema{} - if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoV002Model); err != nil { - t.Errorf("could not convert body into intoto type: %v", err) - } - if intotoV002Model.Content.Hash == nil { - t.Errorf("could not find hash over attestation %v", intotoV002Model) - } - recordedPayloadHash, err := hex.DecodeString(*intotoV002Model.Content.PayloadHash.Value) - if err != nil { - t.Errorf("error converting attestation hash to []byte: %v", err) - } - - if !bytes.Equal(attHash[:], recordedPayloadHash) { - t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]), - *intotoV002Model.Content.PayloadHash.Value)) - } - - out = runCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", ecdsapubKeyPath, "--public-key", rsapubKeyPath) - outputContains(t, out, "Entry already exists") -} - -func TestTimestampArtifact(t *testing.T) { - var out string - out = runCli(t, "upload", "--type", "rfc3161", "--artifact", "test.tsr") - outputContains(t, out, "Created entry at") - uuid := getUUIDFromTimestampOutput(t, out) - - artifactBytes, err := ioutil.ReadFile("test.tsr") - if err != nil { - t.Error(err) - } - sha := sha256.Sum256(artifactBytes) - - out = runCli(t, "upload", "--type", "rfc3161", "--artifact", "test.tsr") - outputContains(t, out, "Entry already exists") - - out = runCli(t, "search", "--artifact", "test.tsr") - outputContains(t, out, uuid) - - out = runCli(t, "search", "--sha", fmt.Sprintf("sha256:%s", hex.EncodeToString(sha[:]))) - outputContains(t, out, uuid) -} - func TestSearchSHA512(t *testing.T) { sha512 := "c7694a1112ea1404a3c5852bdda04c2cc224b3567ef6ceb8204dbf2b382daacfc6837ee2ed9d5b82c90b880a3c7289778dbd5a8c2c08193459bcf7bd44581ed0" var out string @@ -613,25 +319,25 @@ func TestSignedEntryTimestamp(t *testing.T) { } } -func TestGetNonExistantIndex(t *testing.T) { +func TestGetNonExistentIndex(t *testing.T) { // this index is extremely likely to not exist out := runCliErr(t, "get", "--log-index", "100000000") outputContains(t, out, "404") } -func TestVerifyNonExistantIndex(t *testing.T) { +func TestVerifyNonExistentIndex(t *testing.T) { // this index is extremely likely to not exist out := runCliErr(t, "verify", "--log-index", "100000000") outputContains(t, out, "entry in log cannot be located") } -func TestGetNonExistantUUID(t *testing.T) { +func TestGetNonExistentUUID(t *testing.T) { // this uuid is extremely likely to not exist out := runCliErr(t, "get", "--uuid", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") outputContains(t, out, "404") } -func TestVerifyNonExistantUUID(t *testing.T) { +func TestVerifyNonExistentUUID(t *testing.T) { // this uuid is extremely likely to not exist out := runCliErr(t, "verify", "--uuid", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") outputContains(t, out, "entry in log cannot be located") From 1a2c4fc75a7e7c4bb870eb6d3eff6a2143b79f82 Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Sun, 4 Dec 2022 13:34:48 -0600 Subject: [PATCH 3/6] Moved the server tests to server folder - Moved the server tests to server folder. Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- .github/workflows/main.yml | 4 +-- .gitignore | 1 + cmd/rekor-server/e2e_test.go | 52 ++++++++++++++++++++++++++++++++++ pkg/e2e-test.sh => e2e-test.sh | 2 +- 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 cmd/rekor-server/e2e_test.go rename pkg/e2e-test.sh => e2e-test.sh (96%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d01bb56f2..a7ccea5e6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -90,8 +90,8 @@ jobs: - name: CLI run: ./tests/e2e-test.sh - - name: PKG-CLI # this will a WIP to move all the CLI tests to the pkg repo - run: ./pkg/e2e-test.sh + - name: Refactor-e2e # this will a WIP to move all the tests to respective packages + run: ./e2e-test.sh - name: Upload logs if they exist uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1 if: failure() diff --git a/.gitignore b/.gitignore index d92718fab..1a5bec0b6 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ logid !rekor-cli/ rekor-cli rekor-server +!rekor-server/ /tests/rekor-server /server swagger diff --git a/cmd/rekor-server/e2e_test.go b/cmd/rekor-server/e2e_test.go new file mode 100644 index 000000000..961d32d49 --- /dev/null +++ b/cmd/rekor-server/e2e_test.go @@ -0,0 +1,52 @@ +// +// Copyright 2022 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 main + +import ( + "io/ioutil" + "path/filepath" + "testing" + + "github.com/sigstore/rekor/pkg/util" +) + +func TestDuplicates(t *testing.T) { + artifactPath := filepath.Join(t.TempDir(), "artifact") + sigPath := filepath.Join(t.TempDir(), "signature.asc") + + util.CreatedPGPSignedArtifact(t, artifactPath, sigPath) + + // Write the public key to a file + pubPath := filepath.Join(t.TempDir(), "pubKey.asc") + if err := ioutil.WriteFile(pubPath, []byte(util.PubKey), 0644); err != nil { + t.Fatal(err) + } + + // Now upload to rekor! + out := util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) + util.OutputContains(t, out, "Created entry at") + + // Now upload the same one again, we should get a dupe entry. + out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) + util.OutputContains(t, out, "Entry already exists") + + // Now do a new one, we should get a new entry + util.CreatedPGPSignedArtifact(t, artifactPath, sigPath) + out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) + util.OutputContains(t, out, "Created entry at") +} diff --git a/pkg/e2e-test.sh b/e2e-test.sh similarity index 96% rename from pkg/e2e-test.sh rename to e2e-test.sh index 51e42b814..5d494e7a6 100755 --- a/pkg/e2e-test.sh +++ b/e2e-test.sh @@ -50,7 +50,7 @@ REKORTMPDIR="$(mktemp -d -t rekor_test.XXXXXX)" cp $dir/rekor-cli $REKORTMPDIR/rekor-cli touch $REKORTMPDIR.rekor.yaml trap "rm -rf $REKORTMPDIR" EXIT -if ! REKORTMPDIR=$REKORTMPDIR go test -tags=e2e ./pkg/...; then +if ! REKORTMPDIR=$REKORTMPDIR go test -tags=e2e $(go list ./... | grep -v ./tests) ; then docker-compose logs --no-color > /tmp/docker-compose.log exit 1 fi From acf2b7f16f8279a8f4c9f22a4356e5ecc7553ccc Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Tue, 6 Dec 2022 10:16:32 -0600 Subject: [PATCH 4/6] Moved additional tests to server Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- cmd/rekor-server/e2e_test.go | 76 ++++++++++++++++++++++++++++++++++++ tests/e2e_test.go | 74 ----------------------------------- 2 files changed, 76 insertions(+), 74 deletions(-) diff --git a/cmd/rekor-server/e2e_test.go b/cmd/rekor-server/e2e_test.go index 961d32d49..a6fc220d0 100644 --- a/cmd/rekor-server/e2e_test.go +++ b/cmd/rekor-server/e2e_test.go @@ -18,8 +18,13 @@ package main import ( + "bufio" + "fmt" "io/ioutil" + "net/http" "path/filepath" + "regexp" + "strconv" "testing" "github.com/sigstore/rekor/pkg/util" @@ -50,3 +55,74 @@ func TestDuplicates(t *testing.T) { out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) util.OutputContains(t, out, "Created entry at") } + +// Smoke test to ensure we're publishing and recording metrics when an API is +// called. +// TODO: use a more robust test approach here e.g. prometheus client-based? +// TODO: cover all endpoints to make sure none are dropped. +func TestMetricsCounts(t *testing.T) { + latencyMetric := "rekor_latency_by_api_count{method=\"GET\",path=\"/api/v1/log\"}" + qpsMetric := "rekor_qps_by_api{code=\"200\",method=\"GET\",path=\"/api/v1/log\"}" + + latencyCount, err := getRekorMetricCount(latencyMetric, t) + if err != nil { + t.Fatal(err) + } + + qpsCount, err := getRekorMetricCount(qpsMetric, t) + if err != nil { + t.Fatal(err) + } + + resp, err := http.Get("http://localhost:3000/api/v1/log") + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + + latencyCount2, err := getRekorMetricCount(latencyMetric, t) + if err != nil { + t.Fatal(err) + } + + qpsCount2, err := getRekorMetricCount(qpsMetric, t) + if err != nil { + t.Fatal(err) + } + + if latencyCount2-latencyCount != 1 { + t.Error("rekor_latency_by_api_count did not increment") + } + + if qpsCount2-qpsCount != 1 { + t.Error("rekor_qps_by_api did not increment") + } +} +func getRekorMetricCount(metricLine string, t *testing.T) (int, error) { + re, err := regexp.Compile(fmt.Sprintf("^%s.*([0-9]+)$", regexp.QuoteMeta(metricLine))) + if err != nil { + return 0, err + } + + resp, err := http.Get("http://localhost:2112/metrics") + if err != nil { + return 0, err + } + defer resp.Body.Close() + + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + match := re.FindStringSubmatch(scanner.Text()) + if len(match) != 2 { + continue + } + + result, err := strconv.Atoi(match[1]) + if err != nil { + return 0, nil + } + t.Log("Matched metric line: " + scanner.Text()) + return result, nil + } + return 0, nil +} diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 34de448e2..9b4537ff4 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -18,7 +18,6 @@ package e2e import ( - "bufio" "bytes" "context" "crypto" @@ -31,7 +30,6 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "strconv" "strings" "testing" @@ -663,78 +661,6 @@ func TestSearchValidateTreeID(t *testing.T) { } } -func getRekorMetricCount(metricLine string, t *testing.T) (int, error) { - re, err := regexp.Compile(fmt.Sprintf("^%s.*([0-9]+)$", regexp.QuoteMeta(metricLine))) - if err != nil { - return 0, err - } - - resp, err := http.Get("http://localhost:2112/metrics") - if err != nil { - return 0, err - } - defer resp.Body.Close() - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - match := re.FindStringSubmatch(scanner.Text()) - if len(match) != 2 { - continue - } - - result, err := strconv.Atoi(match[1]) - if err != nil { - return 0, nil - } - t.Log("Matched metric line: " + scanner.Text()) - return result, nil - } - return 0, nil -} - -// Smoke test to ensure we're publishing and recording metrics when an API is -// called. -// TODO: use a more robust test approach here e.g. prometheus client-based? -// TODO: cover all endpoints to make sure none are dropped. -func TestMetricsCounts(t *testing.T) { - latencyMetric := "rekor_latency_by_api_count{method=\"GET\",path=\"/api/v1/log\"}" - qpsMetric := "rekor_qps_by_api{code=\"200\",method=\"GET\",path=\"/api/v1/log\"}" - - latencyCount, err := getRekorMetricCount(latencyMetric, t) - if err != nil { - t.Fatal(err) - } - - qpsCount, err := getRekorMetricCount(qpsMetric, t) - if err != nil { - t.Fatal(err) - } - - resp, err := http.Get("http://localhost:3000/api/v1/log") - if err != nil { - t.Fatal(err) - } - resp.Body.Close() - - latencyCount2, err := getRekorMetricCount(latencyMetric, t) - if err != nil { - t.Fatal(err) - } - - qpsCount2, err := getRekorMetricCount(qpsMetric, t) - if err != nil { - t.Fatal(err) - } - - if latencyCount2-latencyCount != 1 { - t.Error("rekor_latency_by_api_count did not increment") - } - - if qpsCount2-qpsCount != 1 { - t.Error("rekor_qps_by_api did not increment") - } -} - // TestSearchLogQuerySingleShard provides coverage testing on the searchLogQuery endpoint within a single shard func TestSearchLogQuerySingleShard(t *testing.T) { From d9eafe5a78e105ea4736376483da0794c8552b08 Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Wed, 7 Dec 2022 16:03:04 -0600 Subject: [PATCH 5/6] Refactored the hardcoded rekor server address to use the rekorserver func Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- tests/e2e_test.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 9b4537ff4..611f63a3f 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -349,7 +349,7 @@ func TestVerifyNonExistentUUID(t *testing.T) { t.Fatal(err) } body := fmt.Sprintf("{\"entryUUIDs\":[\"%s\"]}", entryID.ReturnEntryIDString()) - resp, err := http.Post("http://localhost:3000/api/v1/log/entries/retrieve", + resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewReader([]byte(body))) if err != nil { @@ -476,7 +476,7 @@ func TestInclusionProofRace(t *testing.T) { func TestHostnameInSTH(t *testing.T) { // get ID of container rekorContainerID := strings.Trim(run(t, "", "docker", "ps", "-q", "-f", "name=rekor-server"), "\n") - resp, err := http.Get("http://localhost:3000/api/v1/log") + resp, err := http.Get(fmt.Sprintf("%s/api/v1/log", rekorServer())) if err != nil { t.Fatal(err) } @@ -517,7 +517,7 @@ func TestSearchQueryLimit(t *testing.T) { for _, test := range tests { t.Run(test.description, func(t *testing.T) { b := bytes.NewReader(getBody(t, test.limit)) - resp, err := http.Post("http://localhost:3000/api/v1/log/entries/retrieve", "application/json", b) + resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", b) if err != nil { t.Fatal(err) } @@ -546,7 +546,7 @@ func TestSearchQueryMalformedEntry(t *testing.T) { t.Fatal(err) } body := fmt.Sprintf("{\"entries\":[\"%s\"]}", b) - resp, err := http.Post("http://localhost:3000/api/v1/log/entries/retrieve", + resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(body))) if err != nil { @@ -569,7 +569,7 @@ func TestSearchQueryNonExistentEntry(t *testing.T) { } body := fmt.Sprintf("{\"entries\":[%s]}", b) t.Log(string(body)) - resp, err := http.Post("http://localhost:3000/api/v1/log/entries/retrieve", + resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(body))) if err != nil { @@ -633,7 +633,7 @@ func TestSearchValidateTreeID(t *testing.T) { t.Fatal(err) } body := "{\"entryUUIDs\":[\"%s\"]}" - resp, err := http.Post("http://localhost:3000/api/v1/log/entries/retrieve", "application/json", bytes.NewBuffer([]byte(fmt.Sprintf(body, entryID.ReturnEntryIDString())))) + resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(fmt.Sprintf(body, entryID.ReturnEntryIDString())))) if err != nil { t.Fatal(err) } @@ -647,7 +647,8 @@ func TestSearchValidateTreeID(t *testing.T) { if err != nil { t.Fatal(err) } - resp, err = http.Post("http://localhost:3000/api/v1/log/entries/retrieve", "application/json", bytes.NewBuffer([]byte(fmt.Sprintf(body, entryID.ReturnEntryIDString())))) + + resp, err = http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(fmt.Sprintf(body, entryID.ReturnEntryIDString())))) if err != nil { t.Fatal(err) } @@ -1070,7 +1071,7 @@ func TestSearchLogQuerySingleShard(t *testing.T) { } for _, test := range testCases { - rekorClient, err := client.GetRekorClient("http://localhost:3000", client.WithRetryCount(0)) + rekorClient, err := client.GetRekorClient(rekorServer(), client.WithRetryCount(0)) if err != nil { t.Fatal(err) } From 3a836ecce4020d051a85291b09d856f871c076e9 Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Thu, 8 Dec 2022 18:15:43 -0600 Subject: [PATCH 6/6] Moved additional tests Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- cmd/rekor-server/e2e_test.go | 95 ++++++++++++++++++++++++++++++++++++ tests/e2e_test.go | 5 -- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/cmd/rekor-server/e2e_test.go b/cmd/rekor-server/e2e_test.go index a6fc220d0..765fd8409 100644 --- a/cmd/rekor-server/e2e_test.go +++ b/cmd/rekor-server/e2e_test.go @@ -19,12 +19,18 @@ package main import ( "bufio" + "crypto/sha256" + "encoding/hex" + "encoding/json" "fmt" + "github.com/sigstore/rekor/pkg/sharding" "io/ioutil" "net/http" + "os" "path/filepath" "regexp" "strconv" + "strings" "testing" "github.com/sigstore/rekor/pkg/util" @@ -99,6 +105,7 @@ func TestMetricsCounts(t *testing.T) { } } func getRekorMetricCount(metricLine string, t *testing.T) (int, error) { + t.Helper() re, err := regexp.Compile(fmt.Sprintf("^%s.*([0-9]+)$", regexp.QuoteMeta(metricLine))) if err != nil { return 0, err @@ -126,3 +133,91 @@ func getRekorMetricCount(metricLine string, t *testing.T) (int, error) { } return 0, nil } +func TestEnvVariableValidation(t *testing.T) { + os.Setenv("REKOR_FORMAT", "bogus") + defer os.Unsetenv("REKOR_FORMAT") + + util.RunCliErr(t, "loginfo") +} +func TestGetCLI(t *testing.T) { + // Create something and add it to the log + artifactPath := filepath.Join(t.TempDir(), "artifact") + sigPath := filepath.Join(t.TempDir(), "signature.asc") + t.Cleanup(func() { + os.Remove(artifactPath) + os.Remove(sigPath) + }) + util.CreatedPGPSignedArtifact(t, artifactPath, sigPath) + + // Write the public key to a file + pubPath := filepath.Join(t.TempDir(), "pubKey.asc") + if err := ioutil.WriteFile(pubPath, []byte(util.PubKey), 0644); err != nil { + t.Error(err) + } + t.Cleanup(func() { + os.Remove(pubPath) + }) + out := util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) + util.OutputContains(t, out, "Created entry at") + + uuid, err := sharding.GetUUIDFromIDString(util.GetUUIDFromUploadOutput(t, out)) + if err != nil { + t.Error(err) + } + + // since we at least have 1 valid entry, check the log at index 0 + util.RunCli(t, "get", "--log-index", "0") + + out = util.RunCli(t, "get", "--format=json", "--uuid", uuid) + + // The output here should be in JSON with this structure: + g := util.GetOut{} + if err := json.Unmarshal([]byte(out), &g); err != nil { + t.Error(err) + } + + if g.IntegratedTime == 0 { + t.Errorf("Expected IntegratedTime to be set. Got %s", out) + } + // Get it with the logindex as well + util.RunCli(t, "get", "--format=json", "--log-index", strconv.Itoa(g.LogIndex)) + + // check index via the file and public key to ensure that the index has updated correctly + out = util.RunCli(t, "search", "--artifact", artifactPath) + util.OutputContains(t, out, uuid) + + out = util.RunCli(t, "search", "--public-key", pubPath) + util.OutputContains(t, out, uuid) + + artifactBytes, err := ioutil.ReadFile(artifactPath) + if err != nil { + t.Error(err) + } + sha := sha256.Sum256(artifactBytes) + + out = util.RunCli(t, "search", "--sha", fmt.Sprintf("sha256:%s", hex.EncodeToString(sha[:]))) + util.OutputContains(t, out, uuid) + + // Exercise GET with the new EntryID (TreeID + UUID) + tid := getTreeID(t) + entryID, err := sharding.CreateEntryIDFromParts(fmt.Sprintf("%x", tid), uuid) + if err != nil { + t.Error(err) + } + out = util.RunCli(t, "get", "--format=json", "--uuid", entryID.ReturnEntryIDString()) +} + +func getTreeID(t *testing.T) int64 { + t.Helper() + out := util.RunCli(t, "loginfo") + tidStr := strings.TrimSpace(strings.Split(out, "TreeID: ")[1]) + tid, err := strconv.ParseInt(tidStr, 10, 64) + if err != nil { + t.Errorf(err.Error()) + } + t.Log("Tree ID:", tid) + return tid +} +func TestSearchNoEntriesRC1(t *testing.T) { + util.RunCliErr(t, "search", "--email", "noone@internetz.com") +} diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 611f63a3f..ac1330e8b 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -193,11 +193,6 @@ func TestGetCLI(t *testing.T) { } out = runCli(t, "get", "--format=json", "--uuid", entryID.ReturnEntryIDString()) } - -func TestSearchNoEntriesRC1(t *testing.T) { - runCliErr(t, "search", "--email", "noone@internetz.com") -} - func TestSearchSHA512(t *testing.T) { sha512 := "c7694a1112ea1404a3c5852bdda04c2cc224b3567ef6ceb8204dbf2b382daacfc6837ee2ed9d5b82c90b880a3c7289778dbd5a8c2c08193459bcf7bd44581ed0" var out string