diff --git a/pkg/attest/sign.go b/pkg/attest/sign.go index ec1c9ca..ea8846a 100644 --- a/pkg/attest/sign.go +++ b/pkg/attest/sign.go @@ -15,7 +15,6 @@ import ( "github.com/google/go-containerregistry/pkg/v1/static" "github.com/google/go-containerregistry/pkg/v1/types" intoto "github.com/in-toto/in-toto-golang/in_toto" - ociv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/secure-systems-lab/go-securesystemslib/dsse" ) @@ -81,44 +80,17 @@ func SignIndexAttestations(ctx context.Context, idx v1.ImageIndex, signer dsse.S } ann[InTotoReferenceLifecycleStage] = LifecycleStageExperimental - var env *attestation.Envelope - var mediaType string - switch opts.EnvelopeStyle { - case OCIContentDescriptor: - // Ensure we sign just the digest, size, and media type - payloadDesc := v1.Descriptor{ - Digest: layerDesc.Digest, - Size: layerDesc.Size, - MediaType: layerDesc.MediaType, - } - payload, err := json.Marshal(payloadDesc) - if err != nil { - return nil, fmt.Errorf("failed to marshal descriptor: %w", err) - } - env, err = attestation.SignDSSE(ctx, payload, ociv1.MediaTypeDescriptor, signer) - if err != nil { - return nil, fmt.Errorf("failed to sign statement: %w", err) - } - ann[oci.DockerReferenceDigest] = layerDesc.Digest.String() - // this is a reference type - opts.Replace = false - mediaType = attestation.OCIDescriptorDSSEMediaType - case EmbeddedDSSE: - payload, err := json.Marshal(stmt) - if err != nil { - return nil, fmt.Errorf("failed to marshal statement: %w", err) - } - env, err = attestation.SignDSSE(ctx, payload, intoto.PayloadType, signer) - if err != nil { - return nil, fmt.Errorf("failed to sign statement: %w", err) - } - mediaType, err = attestation.DSSEMediaType(stmt.PredicateType) - if err != nil { - return nil, fmt.Errorf("failed to get DSSE media type: %w", err) - } - - default: - return nil, fmt.Errorf("unknown envelope style %q", opts.EnvelopeStyle) + payload, err := json.Marshal(stmt) + if err != nil { + return nil, fmt.Errorf("failed to marshal statement: %w", err) + } + env, err := attestation.SignDSSE(ctx, payload, intoto.PayloadType, signer) + if err != nil { + return nil, fmt.Errorf("failed to sign statement: %w", err) + } + mediaType, err := attestation.DSSEMediaType(stmt.PredicateType) + if err != nil { + return nil, fmt.Errorf("failed to get DSSE media type: %w", err) } data, err := json.Marshal(env) diff --git a/pkg/attest/sign_test.go b/pkg/attest/sign_test.go index 3b7e39a..c8b726d 100644 --- a/pkg/attest/sign_test.go +++ b/pkg/attest/sign_test.go @@ -35,18 +35,14 @@ func TestSignVerifyOCILayout(t *testing.T) { TestImage string expectedStatements int expectedAttestations int - envelopeStyle envelopeStyle replace bool }{ - {"signed replaced (does nothing)", UnsignedTestImage, 4, 4, OCIContentDescriptor, true}, - {"without replace", UnsignedTestImage, 4, 4, OCIContentDescriptor, false}, - {"embedded", UnsignedTestImage, 0, 6, EmbeddedDSSE, true}, - {"embedded", UnsignedTestImage, 4, 6, EmbeddedDSSE, false}, - + {"signed replaced (does nothing)", UnsignedTestImage, 0, 6, true}, + {"without replace", UnsignedTestImage, 4, 6, false}, // image without provenance doesn't fail - {"no provenance (replace)", NoProvenanceImage, 0, 4, EmbeddedDSSE, true}, - {"no provenance (no replace)", NoProvenanceImage, 2, 4, EmbeddedDSSE, false}, + {"no provenance (replace)", NoProvenanceImage, 0, 4, true}, + {"no provenance (no replace)", NoProvenanceImage, 2, 4, false}, } policyResolver := &policy.PolicyOptions{ LocalPolicyDir: LocalPolicyDir, @@ -56,8 +52,7 @@ func TestSignVerifyOCILayout(t *testing.T) { tempDir := test.CreateTempDir(t, "", TestTempDir) outputLayout := tempDir opts := &SigningOptions{ - Replace: tc.replace, - EnvelopeStyle: tc.envelopeStyle, + Replace: tc.replace, VSAOptions: &attestation.VSAOptions{ BuildLevel: "SLSA_BUILD_LEVEL_3", PolicyURI: "https://docker.com/attest/policy", @@ -94,39 +89,22 @@ func TestSignVerifyOCILayout(t *testing.T) { vsas, err := test.ExtractAnnotatedStatements(tempDir, mt) assert.NoError(t, err) assert.Equalf(t, len(vsas), 2, "expected %d vsa statement, got %d", 2, len(vsas)) + var allEnvelopes []*test.AnnotatedStatement + for _, predicate := range []string{intoto.PredicateSPDX, v02.PredicateSLSAProvenance, attestation.VSAPredicateType} { + mt, _ := attestation.DSSEMediaType(predicate) + statements, err := test.ExtractAnnotatedStatements(tempDir, mt) + assert.NoError(t, err) + allEnvelopes = append(allEnvelopes, statements...) - switch tc.envelopeStyle { - case OCIContentDescriptor: - { - statements, err := test.ExtractAnnotatedStatements(tempDir, intoto.PayloadType) - assert.NoError(t, err) - assert.Equalf(t, tc.expectedStatements, len(statements), "expected %d statement, got %d", tc.expectedStatements, len(statements)) - - statements, err = test.ExtractAnnotatedStatements(tempDir, attestation.OCIDescriptorDSSEMediaType) - assert.NoError(t, err) - - assert.Equalf(t, tc.expectedAttestations, len(statements), "expected %d attestations, got %d", tc.expectedAttestations, len(statements)) - } - case EmbeddedDSSE: - { - var allEnvelopes []*test.AnnotatedStatement - for _, predicate := range []string{intoto.PredicateSPDX, v02.PredicateSLSAProvenance, attestation.VSAPredicateType} { - mt, _ := attestation.DSSEMediaType(predicate) - statements, err := test.ExtractAnnotatedStatements(tempDir, mt) - assert.NoError(t, err) - allEnvelopes = append(allEnvelopes, statements...) - - for _, stmt := range statements { - assert.Equalf(t, predicate, stmt.Annotations[oci.InTotoPredicateType], "expected predicate-type annotation to be set to %s, got %s", predicate, stmt.Annotations[oci.InTotoPredicateType]) - assert.Equalf(t, LifecycleStageExperimental, stmt.Annotations[InTotoReferenceLifecycleStage], "expected reference lifecycle stage annotation to be set to %s, got %s", LifecycleStageExperimental, stmt.Annotations[InTotoReferenceLifecycleStage]) - } - } - assert.Equalf(t, tc.expectedAttestations, len(allEnvelopes), "expected %d attestations, got %d", tc.expectedAttestations, len(allEnvelopes)) - statements, err := test.ExtractAnnotatedStatements(tempDir, intoto.PayloadType) - assert.NoError(t, err) - assert.Equalf(t, tc.expectedStatements, len(statements), "expected %d statement, got %d", tc.expectedStatements, len(statements)) + for _, stmt := range statements { + assert.Equalf(t, predicate, stmt.Annotations[oci.InTotoPredicateType], "expected predicate-type annotation to be set to %s, got %s", predicate, stmt.Annotations[oci.InTotoPredicateType]) + assert.Equalf(t, LifecycleStageExperimental, stmt.Annotations[InTotoReferenceLifecycleStage], "expected reference lifecycle stage annotation to be set to %s, got %s", LifecycleStageExperimental, stmt.Annotations[InTotoReferenceLifecycleStage]) } } + assert.Equalf(t, tc.expectedAttestations, len(allEnvelopes), "expected %d attestations, got %d", tc.expectedAttestations, len(allEnvelopes)) + statements, err := test.ExtractAnnotatedStatements(tempDir, intoto.PayloadType) + assert.NoError(t, err) + assert.Equalf(t, tc.expectedStatements, len(statements), "expected %d statement, got %d", tc.expectedStatements, len(statements)) }) } } diff --git a/pkg/attest/types.go b/pkg/attest/types.go index e7e1963..a837e8d 100644 --- a/pkg/attest/types.go +++ b/pkg/attest/types.go @@ -1,33 +1,15 @@ package attest import ( - "fmt" - "github.com/docker/attest/pkg/attestation" ) -type envelopeStyle string - const ( - OCIContentDescriptor envelopeStyle = "oci-content-descriptor" - EmbeddedDSSE envelopeStyle = "embedded-dsse" - InTotoReferenceLifecycleStage = "vnd.docker.lifecycle-stage" - LifecycleStageExperimental = "experimental" + InTotoReferenceLifecycleStage = "vnd.docker.lifecycle-stage" + LifecycleStageExperimental = "experimental" ) type SigningOptions struct { - Replace bool - EnvelopeStyle envelopeStyle - VSAOptions *attestation.VSAOptions -} - -func EnvelopeStyle(style string) (envelopeStyle, error) { - switch style { - case string(OCIContentDescriptor): - return OCIContentDescriptor, nil - case string(EmbeddedDSSE): - return EmbeddedDSSE, nil - default: - return "", fmt.Errorf("unknown envelope style %q", style) - } + Replace bool + VSAOptions *attestation.VSAOptions }