Skip to content

Commit

Permalink
Merge branch 'main' into dirhash-glob
Browse files Browse the repository at this point in the history
  • Loading branch information
matglas committed May 20, 2024
2 parents bb66b68 + cb6a006 commit 16026a4
Show file tree
Hide file tree
Showing 29 changed files with 483 additions and 69 deletions.
64 changes: 63 additions & 1 deletion cmd/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package cmd
import (
"context"
"crypto"
"crypto/x509"
"errors"
"fmt"
"os"
Expand All @@ -26,6 +27,7 @@ import (
"github.com/in-toto/go-witness/cryptoutil"
"github.com/in-toto/go-witness/log"
"github.com/in-toto/go-witness/source"
"github.com/in-toto/go-witness/timestamp"
archivista_client "github.com/in-toto/witness/internal/archivista"
"github.com/in-toto/witness/internal/policy"
"github.com/in-toto/witness/options"
Expand All @@ -46,6 +48,10 @@ func VerifyCmd() *cobra.Command {
SilenceUsage: true,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
if cmd.Flags().Lookup("policy-ca").Changed {
log.Warn("The flag `--policy-ca` is deprecated and will be removed in a future release. Please use `--policy-ca-root` and `--policy-ca-intermediate` instead.")
}

verifiers, err := loadVerifiers(cmd.Context(), vo.VerifierOptions, vo.KMSVerifierProviderOptions, providersFromFlags("verifier", cmd.Flags()))
if err != nil {
return fmt.Errorf("failed to load signer: %w", err)
Expand Down Expand Up @@ -76,7 +82,7 @@ func runVerify(ctx context.Context, vo options.VerifyOptions, verifiers ...crypt
collectionSource = source.NewMultiSource(collectionSource, source.NewArchvistSource(archivistaClient))
}

if vo.KeyPath == "" && len(vo.CAPaths) == 0 && len(verifiers) == 0 {
if vo.KeyPath == "" && len(vo.PolicyCARootPaths) == 0 && len(verifiers) == 0 {
return fmt.Errorf("must supply either a public key, CA certificates or a verifier")
}

Expand All @@ -99,6 +105,57 @@ func runVerify(ctx context.Context, vo options.VerifyOptions, verifiers ...crypt
verifiers = append(verifiers, v)
}

var policyRoots []*x509.Certificate
if len(vo.PolicyCARootPaths) > 0 {
for _, caPath := range vo.PolicyCARootPaths {
caFile, err := os.ReadFile(caPath)
if err != nil {
return fmt.Errorf("failed to read root CA certificate file: %w", err)
}

cert, err := cryptoutil.TryParseCertificate(caFile)
if err != nil {
return fmt.Errorf("failed to parse root CA certificate: %w", err)
}

policyRoots = append(policyRoots, cert)
}
}

var policyIntermediates []*x509.Certificate
if len(vo.PolicyCAIntermediatePaths) > 0 {
for _, caPath := range vo.PolicyCAIntermediatePaths {
caFile, err := os.ReadFile(caPath)
if err != nil {
return fmt.Errorf("failed to read intermediate CA certificate file: %w", err)
}

cert, err := cryptoutil.TryParseCertificate(caFile)
if err != nil {
return fmt.Errorf("failed to parse intermediate CA certificate: %w", err)
}

policyRoots = append(policyIntermediates, cert)
}
}

ptsVerifiers := make([]timestamp.TimestampVerifier, 0)
if len(vo.PolicyTimestampServers) > 0 {
for _, server := range vo.PolicyTimestampServers {
f, err := os.ReadFile(server)
if err != nil {
return fmt.Errorf("failed to open Timestamp Server CA certificate file: %w", err)
}

cert, err := cryptoutil.TryParseCertificate(f)
if err != nil {
return fmt.Errorf("failed to parse Timestamp Server CA certificate: %w", err)
}

ptsVerifiers = append(ptsVerifiers, timestamp.NewVerifier(timestamp.VerifyWithCerts([]*x509.Certificate{cert})))
}
}

policyEnvelope, err := policy.LoadPolicy(ctx, vo.PolicyFilePath, archivista_client.NewArchivistaClient(vo.ArchivistaOptions.Url, archivistaClient))
if err != nil {
return fmt.Errorf("failed to open policy file: %w", err)
Expand Down Expand Up @@ -143,6 +200,11 @@ func runVerify(ctx context.Context, vo options.VerifyOptions, verifiers ...crypt
verifiers,
witness.VerifyWithSubjectDigests(subjects),
witness.VerifyWithCollectionSource(collectionSource),
witness.VerifyWithPolicyTimestampAuthorities(ptsVerifiers),
witness.VerifyWithPolicyCARoots(policyRoots),
witness.VerifyWithPolicyCAIntermediates(policyIntermediates),
witness.VerifyWithPolicyCertConstraints(vo.PolicyCommonName, vo.PolicyDNSNames, vo.PolicyEmails, vo.PolicyOrganizations, vo.PolicyURIs),
witness.VerifyWithPolicyFulcioCertExtensions(vo.PolicyFulcioCertExtensions),
)
if err != nil {
if verifiedEvidence.StepResults != nil {
Expand Down
125 changes: 125 additions & 0 deletions cmd/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,69 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestVerifyPolicyWithFulcio(t *testing.T) {
workingDir := t.TempDir()
cwd, err := os.Getwd()
if err != nil {
panic(err)
}

err = os.WriteFile(filepath.Join(workingDir, "fulcio.pem"), []byte(fulciopem), 0644)
if err != nil {
panic(err)
}

err = os.WriteFile(filepath.Join(workingDir, "freetsa.pem"), []byte(freetsapem), 0644)
if err != nil {
panic(err)
}

vo := options.VerifyOptions{
PolicyFilePath: filepath.Join(cwd, "../test/fulcio-policy-signed.json"),
PolicyTimestampServers: []string{filepath.Join(workingDir, "freetsa.pem")},
PolicyCARootPaths: []string{filepath.Join(workingDir, "fulcio.pem")},
AttestationFilePaths: []string{filepath.Join(cwd, "../test/test.json")},
ArtifactFilePath: filepath.Join(cwd, "../test/test.txt"),
PolicyCommonName: "*",
PolicyURIs: []string{"*"},
PolicyDNSNames: []string{"*"},
PolicyEmails: []string{"*"},
PolicyOrganizations: []string{"*"},
}

require.NoError(t, runVerify(context.Background(), vo))
}

// Same test but deliberately missing the CA file path for verifying the policy
func TestVerifyPolicyWrongCAFile(t *testing.T) {
workingDir := t.TempDir()
cwd, err := os.Getwd()
if err != nil {
panic(err)
}

// we're going to write the wrong CA file here to ensure that it fails
err = os.WriteFile(filepath.Join(workingDir, "badca.pem"), []byte(freetsapem), 0644)
if err != nil {
panic(err)
}

err = os.WriteFile(filepath.Join(workingDir, "freetsa.pem"), []byte(freetsapem), 0644)
if err != nil {
panic(err)
}

vo := options.VerifyOptions{
PolicyFilePath: filepath.Join(cwd, "../test/fulcio-policy-signed.json"),
PolicyTimestampServers: []string{filepath.Join(workingDir, "freetsa.pem")},
PolicyCARootPaths: []string{filepath.Join(workingDir, "badca.pem")},
AttestationFilePaths: []string{filepath.Join(cwd, "../test/test.json")},
ArtifactFilePath: filepath.Join(cwd, "../test/test.txt"),
}

require.ErrorContains(t, runVerify(context.Background(), vo), "failed to verify policy: attestors failed with error messages\nfailed to verify policy signature: could not verify policy: no valid signatures for the provided verifiers found for keyids:\n")
}

func TestRunVerifyCA(t *testing.T) {
ca, intermediates, leafcert, leafkey := fullChain(t)

Expand Down Expand Up @@ -359,3 +422,65 @@ func createTestRSAKey() (cryptoutil.Signer, cryptoutil.Verifier, []byte, []byte,

return signer, verifier, pemBytes, privKeyBytes, nil
}

const (
fulciopem = `-----BEGIN CERTIFICATE-----
MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMw
KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y
MTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3Jl
LmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7
XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxex
X69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92j
YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRY
wB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQ
KsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCM
WP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9
TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ
-----END CERTIFICATE-----
`
freetsapem = `-----BEGIN CERTIFICATE-----
MIIH/zCCBeegAwIBAgIJAMHphhYNqOmAMA0GCSqGSIb3DQEBDQUAMIGVMREwDwYD
VQQKEwhGcmVlIFRTQTEQMA4GA1UECxMHUm9vdCBDQTEYMBYGA1UEAxMPd3d3LmZy
ZWV0c2Eub3JnMSIwIAYJKoZIhvcNAQkBFhNidXNpbGV6YXNAZ21haWwuY29tMRIw
EAYDVQQHEwlXdWVyemJ1cmcxDzANBgNVBAgTBkJheWVybjELMAkGA1UEBhMCREUw
HhcNMTYwMzEzMDE1MjEzWhcNNDEwMzA3MDE1MjEzWjCBlTERMA8GA1UEChMIRnJl
ZSBUU0ExEDAOBgNVBAsTB1Jvb3QgQ0ExGDAWBgNVBAMTD3d3dy5mcmVldHNhLm9y
ZzEiMCAGCSqGSIb3DQEJARYTYnVzaWxlemFzQGdtYWlsLmNvbTESMBAGA1UEBxMJ
V3VlcnpidXJnMQ8wDQYDVQQIEwZCYXllcm4xCzAJBgNVBAYTAkRFMIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtgKODjAy8REQ2WTNqUudAnjhlCrpE6ql
mQfNppeTmVvZrH4zutn+NwTaHAGpjSGv4/WRpZ1wZ3BRZ5mPUBZyLgq0YrIfQ5Fx
0s/MRZPzc1r3lKWrMR9sAQx4mN4z11xFEO529L0dFJjPF9MD8Gpd2feWzGyptlel
b+PqT+++fOa2oY0+NaMM7l/xcNHPOaMz0/2olk0i22hbKeVhvokPCqhFhzsuhKsm
q4Of/o+t6dI7sx5h0nPMm4gGSRhfq+z6BTRgCrqQG2FOLoVFgt6iIm/BnNffUr7V
DYd3zZmIwFOj/H3DKHoGik/xK3E82YA2ZulVOFRW/zj4ApjPa5OFbpIkd0pmzxzd
EcL479hSA9dFiyVmSxPtY5ze1P+BE9bMU1PScpRzw8MHFXxyKqW13Qv7LWw4sbk3
SciB7GACbQiVGzgkvXG6y85HOuvWNvC5GLSiyP9GlPB0V68tbxz4JVTRdw/Xn/XT
FNzRBM3cq8lBOAVt/PAX5+uFcv1S9wFE8YjaBfWCP1jdBil+c4e+0tdywT2oJmYB
BF/kEt1wmGwMmHunNEuQNzh1FtJY54hbUfiWi38mASE7xMtMhfj/C4SvapiDN837
gYaPfs8x3KZxbX7C3YAsFnJinlwAUss1fdKar8Q/YVs7H/nU4c4Ixxxz4f67fcVq
M2ITKentbCMCAwEAAaOCAk4wggJKMAwGA1UdEwQFMAMBAf8wDgYDVR0PAQH/BAQD
AgHGMB0GA1UdDgQWBBT6VQ2MNGZRQ0z357OnbJWveuaklzCBygYDVR0jBIHCMIG/
gBT6VQ2MNGZRQ0z357OnbJWveuakl6GBm6SBmDCBlTERMA8GA1UEChMIRnJlZSBU
U0ExEDAOBgNVBAsTB1Jvb3QgQ0ExGDAWBgNVBAMTD3d3dy5mcmVldHNhLm9yZzEi
MCAGCSqGSIb3DQEJARYTYnVzaWxlemFzQGdtYWlsLmNvbTESMBAGA1UEBxMJV3Vl
cnpidXJnMQ8wDQYDVQQIEwZCYXllcm4xCzAJBgNVBAYTAkRFggkAwemGFg2o6YAw
MwYDVR0fBCwwKjAooCagJIYiaHR0cDovL3d3dy5mcmVldHNhLm9yZy9yb290X2Nh
LmNybDCBzwYDVR0gBIHHMIHEMIHBBgorBgEEAYHyJAEBMIGyMDMGCCsGAQUFBwIB
FidodHRwOi8vd3d3LmZyZWV0c2Eub3JnL2ZyZWV0c2FfY3BzLmh0bWwwMgYIKwYB
BQUHAgEWJmh0dHA6Ly93d3cuZnJlZXRzYS5vcmcvZnJlZXRzYV9jcHMucGRmMEcG
CCsGAQUFBwICMDsaOUZyZWVUU0EgdHJ1c3RlZCB0aW1lc3RhbXBpbmcgU29mdHdh
cmUgYXMgYSBTZXJ2aWNlIChTYWFTKTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUH
MAGGG2h0dHA6Ly93d3cuZnJlZXRzYS5vcmc6MjU2MDANBgkqhkiG9w0BAQ0FAAOC
AgEAaK9+v5OFYu9M6ztYC+L69sw1omdyli89lZAfpWMMh9CRmJhM6KBqM/ipwoLt
nxyxGsbCPhcQjuTvzm+ylN6VwTMmIlVyVSLKYZcdSjt/eCUN+41K7sD7GVmxZBAF
ILnBDmTGJmLkrU0KuuIpj8lI/E6Z6NnmuP2+RAQSHsfBQi6sssnXMo4HOW5gtPO7
gDrUpVXID++1P4XndkoKn7Svw5n0zS9fv1hxBcYIHPPQUze2u30bAQt0n0iIyRLz
aWuhtpAtd7ffwEbASgzB7E+NGF4tpV37e8KiA2xiGSRqT5ndu28fgpOY87gD3ArZ
DctZvvTCfHdAS5kEO3gnGGeZEVLDmfEsv8TGJa3AljVa5E40IQDsUXpQLi8G+UC4
1DWZu8EVT4rnYaCw1VX7ShOR1PNCCvjb8S8tfdudd9zhU3gEB0rxdeTy1tVbNLXW
99y90xcwr1ZIDUwM/xQ/noO8FRhm0LoPC73Ef+J4ZBdrvWwauF3zJe33d4ibxEcb
8/pz5WzFkeixYM2nsHhqHsBKw7JPouKNXRnl5IAE1eFmqDyC7G/VT7OF669xM6hb
Ut5G21JE4cNK6NNucS+fzg1JPX0+3VhsYZjj7D5uljRvQXrJ8iHgr/M6j2oLHvTA
I2MLdq2qjZFDOCXsxBxJpbmLGBx9ow6ZerlUxzws2AWv2pk=
-----END CERTIFICATE-----`
)
49 changes: 48 additions & 1 deletion docgen/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"strings"

"github.com/in-toto/witness/cmd"
"github.com/invopop/jsonschema"
"github.com/spf13/cobra/doc"

_ "github.com/in-toto/go-witness"
Expand Down Expand Up @@ -82,7 +83,7 @@ func main() {
os.Exit(1)
}

schemaContent := "## Schema" + "\n```json\n" + indented.String() + "```\n"
schemaContent := "## Schema" + "\n```json\n" + indented.String() + "\n```\n"
err = os.WriteFile(fmt.Sprintf("%s/attestors/%s.json", directory, att.Name()), []byte(indented.String()+"\n "), 0644)
if err != nil {
fmt.Println("Error writing to file:", err)
Expand Down Expand Up @@ -122,4 +123,50 @@ func main() {
log.Printf("Schema for %s written to %s/attestors/%s.md\n", att.Name(), directory, att.Name())

}

log.Println("Generating schema for the Witness Collection")
coll := jsonschema.Reflect(attestation.Collection{})
schemaJson, err := coll.MarshalJSON()
if err != nil {
fmt.Println("Error marshalling JSON schema:", err)
os.Exit(1)
}
var indented bytes.Buffer
err = json.Indent(&indented, schemaJson, "", " ")
if err != nil {
fmt.Println("Error marshalling JSON schema:", err)
os.Exit(1)
}
schemaContent := "## Schema" + "\n```json\n" + indented.String() + "\n```\n"
f, err := os.ReadFile(fmt.Sprintf("%s/concepts/collection.md", directory))
if err != nil {
fmt.Println("Error reading file:", err)
os.Exit(1)
}

// Find the index of "## Schema" string
index := strings.Index(string(f), "## Schema")
if index == -1 {
f = append(f, schemaContent...)

err = os.WriteFile(fmt.Sprintf("%s/concepts/collection.md", directory), f, 0644)
if err != nil {
fmt.Println("Error writing to file:", err)
os.Exit(1)
}
} else {

// Truncate the content to remove everything after "## Schema"
f = f[:index]

f = append(f, schemaContent...)

err = os.WriteFile(fmt.Sprintf("%s/concepts/collection.md", directory), f, 0644)
if err != nil {
fmt.Println("Error writing to file:", err)
os.Exit(1)
}

log.Printf("Schema for collection written to %s/concepts/collection.md\n", directory)
}
}
4 changes: 4 additions & 0 deletions docgen/verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ tmpdir2=$(mktemp -d)
cp docs/commands.md "$tmpdir2/"
mkdir "$tmpdir2/attestors"
mkdir "$tmpdir/attestors"
mkdir "$tmpdir2/concepts"
mkdir "$tmpdir/concepts"
cp docs/attestors/* "$tmpdir2/attestors/"
cp docs/attestors/*.md "$tmpdir/attestors/"
cp docs/concepts/collection.md "$tmpdir2/concepts/"
cp docs/concepts/collection.md "$tmpdir/concepts/"
go run ./docgen --dir "$tmpdir"
echo "###########################################"
echo "If diffs are found, run: make docgen"
Expand Down
3 changes: 2 additions & 1 deletion docs/attestors/aws.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,5 @@ GovCloud regions.
]
}
}
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/command-run.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,5 @@ Linux operating systems and is considered experimental.
]
}
}
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ so take care to not leak secrets stored in environment variables.
]
}
}
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/gcp-iit.md
Original file line number Diff line number Diff line change
Expand Up @@ -573,4 +573,5 @@ against Google's JWKS ([JSON Web Key Set](https://auth0.com/docs/secure/tokens/j
]
}
}
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/git.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,5 @@ The attestor returns the SHA1 ([Secure Hash Algorithm 1](https://en.wikipedia.or
]
}
}
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/github.md
Original file line number Diff line number Diff line change
Expand Up @@ -560,4 +560,5 @@ The [Github](https://github.com/about) Attestor records information about the [G
]
}
}
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/gitlab.md
Original file line number Diff line number Diff line change
Expand Up @@ -575,4 +575,5 @@ instance's JWKS ([JSON Web Key Set](https://auth0.com/docs/secure/tokens/json-we
]
}
}
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/jwt.md
Original file line number Diff line number Diff line change
Expand Up @@ -500,4 +500,5 @@ claims of the JWT. The JWK that was used to verify the JWT is also recorded.
]
}
}
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/link.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,5 @@ The Link Attestor generates an [in-toto Link attestation](https://in-toto.readth
]
}
}
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/material.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ of all files before any changes are made by a command.
"required": [
"Materials"
]
}```
}
```
3 changes: 2 additions & 1 deletion docs/attestors/maven.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@ The [Maven](https://maven.apache.org/) Attestor records project and dependency i
]
}
}
}```
}
```
Loading

0 comments on commit 16026a4

Please sign in to comment.