Merge branch 'main' into fix-oci-layout-referrers

This commit is contained in:
Joel Kamp
2024-08-01 13:48:53 -05:00
committed by GitHub
57 changed files with 532 additions and 517 deletions

View File

@@ -12,7 +12,7 @@ jobs:
id-token: write
strategy:
matrix:
go-version: [1.21.x]
go-version: [1.22.x]
# temp disable windows tests see https://github.com/docker/image-signer-verifier/pull/154
# os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest]

View File

@@ -16,15 +16,15 @@ type MockResolver struct {
Envs []*attestation.Envelope
}
func (r MockResolver) Attestations(ctx context.Context, mediaType string) ([]*attestation.Envelope, error) {
func (r MockResolver) Attestations(_ context.Context, _ string) ([]*attestation.Envelope, error) {
return r.Envs, nil
}
func (r MockResolver) ImageName(ctx context.Context) (string, error) {
func (r MockResolver) ImageName(_ context.Context) (string, error) {
return "library/alpine:latest", nil
}
func (r MockResolver) ImageDescriptor(ctx context.Context) (*v1.Descriptor, error) {
func (r MockResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error) {
digest, err := v1.NewHash("sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620")
if err != nil {
return nil, err
@@ -36,7 +36,7 @@ func (r MockResolver) ImageDescriptor(ctx context.Context) (*v1.Descriptor, erro
}, nil
}
func (r MockResolver) ImagePlatform(ctx context.Context) (*v1.Platform, error) {
func (r MockResolver) ImagePlatform(_ context.Context) (*v1.Platform, error) {
return oci.ParsePlatform("linux/amd64")
}
@@ -46,15 +46,15 @@ type MockRegistryResolver struct {
*MockResolver
}
func (r *MockRegistryResolver) ImageDescriptor(ctx context.Context) (*v1.Descriptor, error) {
func (r *MockRegistryResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error) {
return r.Subject, nil
}
func (r *MockRegistryResolver) ImageName(ctx context.Context) (string, error) {
func (r *MockRegistryResolver) ImageName(_ context.Context) (string, error) {
return r.ImageNameStr, nil
}
func GetMockSigner(ctx context.Context) (dsse.SignerVerifier, error) {
func GetMockSigner(_ context.Context) (dsse.SignerVerifier, error) {
priv, err := os.ReadFile(filepath.Join("..", "..", "test", "testdata", "test-signing-key.pem"))
if err != nil {
return nil, err

View File

@@ -21,12 +21,12 @@ import (
)
const (
USE_MOCK_TL = true
USE_MOCK_KMS = true
USE_MOCK_POLICY = true
UseMockTL = true
UseMockKMS = true
UseMockPolicy = true
AwsRegion = "us-east-1"
AwsKmsKeyArn = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
AWSRegion = "us-east-1"
AWSKMSKeyARN = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
)
func CreateTempDir(t *testing.T, dir, pattern string) string {
@@ -47,7 +47,7 @@ func CreateTempDir(t *testing.T, dir, pattern string) string {
func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
var tl tlog.TL
if USE_MOCK_TL {
if UseMockTL {
tl = tlog.GetMockTL()
} else {
tl = &tlog.RekorTL{}
@@ -55,8 +55,8 @@ func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
ctx := tlog.WithTL(context.Background(), tl)
var policyEvaluator policy.PolicyEvaluator
if USE_MOCK_POLICY {
var policyEvaluator policy.Evaluator
if UseMockPolicy {
policyEvaluator = policy.GetMockPolicy()
} else {
policyEvaluator = policy.NewRegoEvaluator(true)
@@ -66,13 +66,13 @@ func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
var signer dsse.SignerVerifier
var err error
if USE_MOCK_KMS {
if UseMockKMS {
signer, err = GetMockSigner(ctx)
if err != nil {
t.Fatal(err)
}
} else {
signer, err = signerverifier.GetAWSSigner(ctx, AwsKmsKeyArn, AwsRegion)
signer, err = signerverifier.GetAWSSigner(ctx, AWSKMSKeyARN, AWSRegion)
if err != nil {
t.Fatal(err)
}
@@ -95,7 +95,8 @@ func ExtractStatementsFromIndex(idx v1.ImageIndex, mediaType string) ([]*Annotat
var statements []*AnnotatedStatement
for _, mf := range mfs2.Manifests {
for i := range mfs2.Manifests {
mf := &mfs2.Manifests[i]
if mf.Annotations[attestation.DockerReferenceType] != "attestation-manifest" {
continue
}
@@ -124,7 +125,7 @@ func ExtractStatementsFromIndex(idx v1.ImageIndex, mediaType string) ([]*Annotat
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer r.Close()
intotoStatement := new(intoto.Statement)
inTotoStatement := new(intoto.Statement)
var desc *v1.Descriptor
if strings.HasSuffix(string(mt), "+dsse") {
env := new(attestation.Envelope)
@@ -136,7 +137,7 @@ func ExtractStatementsFromIndex(idx v1.ImageIndex, mediaType string) ([]*Annotat
if err != nil {
return nil, fmt.Errorf("failed to decode payload: %w", err)
}
err = json.Unmarshal([]byte(payload), intotoStatement)
err = json.Unmarshal([]byte(payload), inTotoStatement)
if err != nil {
return nil, fmt.Errorf("failed to decode %s statement: %w", mediaType, err)
}
@@ -158,7 +159,7 @@ func ExtractStatementsFromIndex(idx v1.ImageIndex, mediaType string) ([]*Annotat
}
statements = append(statements, &AnnotatedStatement{
OCIDescriptor: desc,
InTotoStatement: intotoStatement,
InTotoStatement: inTotoStatement,
Annotations: annotations,
})
}

View File

@@ -66,7 +66,7 @@ func ExampleSignStatements_remote() {
Add: signedIndex,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OciReferenceTarget: attIdx.Name,
oci.OCIReferenceTarget: attIdx.Name,
},
},
})

View File

@@ -13,7 +13,7 @@ import (
"github.com/docker/attest/pkg/tuf"
)
func createTufClient(outputPath string) (*tuf.TufClient, error) {
func createTufClient(outputPath string) (*tuf.Client, error) {
// using oci tuf metadata and targets
metadataURI := "registry-1.docker.io/docker/tuf-metadata:latest"
targetsURI := "registry-1.docker.io/docker/tuf-targets"
@@ -21,7 +21,7 @@ func createTufClient(outputPath string) (*tuf.TufClient, error) {
// metadataURI := "https://docker.github.io/tuf-staging/metadata"
// targetsURI := "https://docker.github.io/tuf-staging/targets"
return tuf.NewTufClient(embed.RootStaging.Data, outputPath, metadataURI, targetsURI, tuf.NewMockVersionChecker())
return tuf.NewClient(embed.RootStaging.Data, outputPath, metadataURI, targetsURI, tuf.NewMockVersionChecker())
}
func ExampleVerify_remote() {
@@ -41,11 +41,11 @@ func ExampleVerify_remote() {
platform := "linux/amd64"
// configure policy options
opts := &policy.PolicyOptions{
TufClient: tufClient,
opts := &policy.Options{
TUFClient: tufClient,
LocalTargetsDir: filepath.Join(home, ".docker", "policy"), // location to store policy files downloaded from TUF
LocalPolicyDir: "", // overrides TUF policy for local policy files if set
PolicyId: "", // set to ignore policy mapping and select a policy by id
PolicyID: "", // set to ignore policy mapping and select a policy by id
}
src, err := oci.ParseImageSpec(image, oci.WithPlatform(platform))

View File

@@ -9,8 +9,8 @@ import (
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
// this is only relevant if there are (unsigned) in-toto statements
func SignStatements(ctx context.Context, idx v1.ImageIndex, signer dsse.SignerVerifier, opts *attestation.SigningOptions) ([]*attestation.AttestationManifest, error) {
// this is only relevant if there are (unsigned) in-toto statements.
func SignStatements(ctx context.Context, idx v1.ImageIndex, signer dsse.SignerVerifier, opts *attestation.SigningOptions) ([]*attestation.Manifest, error) {
// extract attestation manifests from index
attestationManifests, err := attestation.GetAttestationManifestsFromIndex(idx)
if err != nil {

View File

@@ -52,7 +52,7 @@ func TestSignVerifyOCILayout(t *testing.T) {
{"no provenance (replace)", NoProvenanceImage, 0, 2, true},
{"no provenance (no replace)", NoProvenanceImage, 2, 2, false},
}
policyOpts := &policy.PolicyOptions{
policyOpts := &policy.Options{
LocalPolicyDir: PassPolicyDir,
}
for _, tc := range testCases {
@@ -72,7 +72,7 @@ func TestSignVerifyOCILayout(t *testing.T) {
Add: signedIndex,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OciReferenceTarget: attIdx.Name,
oci.OCIReferenceTarget: attIdx.Name,
},
},
})
@@ -120,7 +120,7 @@ func TestAddSignedLayerAnnotations(t *testing.T) {
testLayer := static.NewLayer(data, types.MediaType(intoto.PayloadType))
mediaType := types.OCIManifestSchema1
opts := &attestation.SigningOptions{}
originalLayer := &attestation.AttestationLayer{
originalLayer := &attestation.Layer{
Layer: testLayer,
Statement: &intoto.Statement{
StatementHeader: intoto.StatementHeader{
@@ -130,11 +130,11 @@ func TestAddSignedLayerAnnotations(t *testing.T) {
Annotations: map[string]string{"test": "test"},
}
manifest := &attestation.AttestationManifest{
manifest := &attestation.Manifest{
OriginalDescriptor: &v1.Descriptor{
MediaType: mediaType,
},
OriginalLayers: []*attestation.AttestationLayer{
OriginalLayers: []*attestation.Layer{
originalLayer,
},
SubjectDescriptor: &v1.Descriptor{},

View File

@@ -30,7 +30,7 @@ func (o Outcome) StringForVSA() (string, error) {
type VerificationResult struct {
Outcome Outcome
Policy *policy.Policy
Input *policy.PolicyInput
Input *policy.Input
VSA *intoto.Statement
Violations []policy.Violation
SubjectDescriptor *v1.Descriptor

View File

@@ -15,7 +15,7 @@ import (
intoto "github.com/in-toto/in-toto-golang/in_toto"
)
func Verify(ctx context.Context, src *oci.ImageSpec, opts *policy.PolicyOptions) (result *VerificationResult, err error) {
func Verify(ctx context.Context, src *oci.ImageSpec, opts *policy.Options) (result *VerificationResult, err error) {
// so that we can resolve mapping from the image name earlier
detailsResolver, err := policy.CreateImageDetailsResolver(src)
if err != nil {
@@ -61,16 +61,16 @@ func Verify(ctx context.Context, src *oci.ImageSpec, opts *policy.PolicyOptions)
return result, nil
}
func toVerificationResult(p *policy.Policy, input *policy.PolicyInput, result *policy.Result) (*VerificationResult, error) {
func toVerificationResult(p *policy.Policy, input *policy.Input, result *policy.Result) (*VerificationResult, error) {
dgst, err := oci.SplitDigest(input.Digest)
if err != nil {
return nil, fmt.Errorf("failed to split digest: %w", err)
}
subject := intoto.Subject{
Name: input.Purl,
Name: input.PURL,
Digest: dgst,
}
resourceUri, err := attestation.ToVSAResourceURI(subject)
resourceURI, err := attestation.ToVSAResourceURI(subject)
if err != nil {
return nil, fmt.Errorf("failed to create resource uri: %w", err)
}
@@ -103,7 +103,7 @@ func toVerificationResult(p *policy.Policy, input *policy.PolicyInput, result *p
ID: result.Summary.Verifier,
},
TimeVerified: time.Now().UTC().Format(time.RFC3339),
ResourceUri: resourceUri,
ResourceURI: resourceURI,
Policy: attestation.VSAPolicy{URI: result.Summary.PolicyURI},
VerificationResult: outcomeStr,
VerifiedLevels: result.Summary.SLSALevels,
@@ -143,9 +143,9 @@ func VerifyAttestations(ctx context.Context, resolver oci.AttestationResolver, p
if err != nil {
return nil, fmt.Errorf("failed to convert ref to purl: %w", err)
}
input := &policy.PolicyInput{
input := &policy.Input{
Digest: digest,
Purl: purl,
PURL: purl,
IsCanonical: canonical,
}
@@ -165,12 +165,12 @@ func VerifyAttestations(ctx context.Context, resolver oci.AttestationResolver, p
return verificationResult, nil
}
func NewAttestationManifest(subject *v1.Descriptor) (*attestation.AttestationManifest, error) {
return &attestation.AttestationManifest{
func NewAttestationManifest(subject *v1.Descriptor) (*attestation.Manifest, error) {
return &attestation.Manifest{
OriginalDescriptor: &v1.Descriptor{
MediaType: "application/vnd.oci.image.manifest.v1+json",
},
OriginalLayers: []*attestation.AttestationLayer{},
OriginalLayers: []*attestation.Layer{},
SubjectDescriptor: subject,
}, nil
}

View File

@@ -50,7 +50,7 @@ func TestVerifyAttestations(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockPE := policy.MockPolicyEvaluator{
EvaluateFunc: func(ctx context.Context, resolver oci.AttestationResolver, pctx *policy.Policy, input *policy.PolicyInput) (*policy.Result, error) {
EvaluateFunc: func(_ context.Context, _ oci.AttestationResolver, _ *policy.Policy, _ *policy.Input) (*policy.Result, error) {
return policy.AllowedResult(), tc.policyEvaluationError
},
}
@@ -89,7 +89,7 @@ func TestVSA(t *testing.T) {
Add: signedIndex,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OciReferenceTarget: attIdx.Name,
oci.OCIReferenceTarget: attIdx.Name,
},
},
})
@@ -97,7 +97,7 @@ func TestVSA(t *testing.T) {
assert.NoError(t, err)
// mocked vsa query should pass
policyOpts := &policy.PolicyOptions{
policyOpts := &policy.Options{
LocalPolicyDir: PassPolicyDir,
}
src, err := oci.ParseImageSpec("oci://"+outputLayout, oci.WithPlatform(LinuxAMD64))
@@ -117,7 +117,8 @@ func TestVSA(t *testing.T) {
assert.Len(t, results.VSA.Subject, 1)
require.IsType(t, attestation.VSAPredicate{}, results.VSA.Predicate)
attestationPredicate := results.VSA.Predicate.(attestation.VSAPredicate)
attestationPredicate, ok := results.VSA.Predicate.(attestation.VSAPredicate)
require.True(t, ok)
assert.Equal(t, "PASSED", attestationPredicate.VerificationResult)
assert.Equal(t, "docker-official-images", attestationPredicate.Verifier.ID)
@@ -146,7 +147,7 @@ func TestVerificationFailure(t *testing.T) {
Add: signedIndex,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OciReferenceTarget: attIdx.Name,
oci.OCIReferenceTarget: attIdx.Name,
},
},
})
@@ -154,7 +155,7 @@ func TestVerificationFailure(t *testing.T) {
assert.NoError(t, err)
// mocked vsa query should fail
policyOpts := &policy.PolicyOptions{
policyOpts := &policy.Options{
LocalPolicyDir: FailPolicyDir,
}
src, err := oci.ParseImageSpec("oci://"+outputLayout, oci.WithPlatform(LinuxAMD64))
@@ -174,7 +175,8 @@ func TestVerificationFailure(t *testing.T) {
assert.Len(t, results.VSA.Subject, 1)
require.IsType(t, attestation.VSAPredicate{}, results.VSA.Predicate)
attestationPredicate := results.VSA.Predicate.(attestation.VSAPredicate)
attestationPredicate, ok := results.VSA.Predicate.(attestation.VSAPredicate)
require.True(t, ok)
assert.Equal(t, "FAILED", attestationPredicate.VerificationResult)
assert.Equal(t, "docker-official-images", attestationPredicate.Verifier.ID)
@@ -227,14 +229,14 @@ func TestSignVerify(t *testing.T) {
Add: signedIndex,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OciReferenceTarget: imageName,
oci.OCIReferenceTarget: imageName,
},
},
})
_, err = layout.Write(outputLayout, idx)
assert.NoError(t, err)
policyOpts := &policy.PolicyOptions{
policyOpts := &policy.Options{
LocalPolicyDir: tc.policyDir,
}
src, err := oci.ParseImageSpec("oci://"+outputLayout, oci.WithPlatform(LinuxAMD64))
@@ -250,7 +252,7 @@ func TestSignVerify(t *testing.T) {
require.NoError(t, err)
expectedPURL, _, err := oci.RefToPURL(attIdx.Name, platform)
require.NoError(t, err)
assert.Equal(t, expectedPURL, results.Input.Purl)
assert.Equal(t, expectedPURL, results.Input.PURL)
})
}
}

View File

@@ -17,19 +17,21 @@ import (
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
// GetAttestationManifestsFromIndex extracts all attestation manifests from an index
func GetAttestationManifestsFromIndex(index v1.ImageIndex) ([]*AttestationManifest, error) {
// GetAttestationManifestsFromIndex extracts all attestation manifests from an index.
func GetAttestationManifestsFromIndex(index v1.ImageIndex) ([]*Manifest, error) {
idx, err := index.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
}
subjects := make(map[string]*v1.Descriptor)
for _, subject := range idx.Manifests {
subjects[subject.Digest.String()] = &subject
for i := range idx.Manifests {
subject := &idx.Manifests[i]
subjects[subject.Digest.String()] = subject
}
var attestationManifests []*AttestationManifest
for _, desc := range idx.Manifests {
var attestationManifests []*Manifest
for i := range idx.Manifests {
desc := idx.Manifests[i]
if desc.Annotations[DockerReferenceType] == AttestationManifestType {
subject := subjects[desc.Annotations[DockerReferenceDigest]]
if subject == nil {
@@ -44,7 +46,7 @@ func GetAttestationManifestsFromIndex(index v1.ImageIndex) ([]*AttestationManife
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
}
attestationManifests = append(attestationManifests,
&AttestationManifest{
&Manifest{
OriginalDescriptor: &desc,
SubjectDescriptor: subject,
OriginalLayers: attestationLayers,
@@ -54,13 +56,13 @@ func GetAttestationManifestsFromIndex(index v1.ImageIndex) ([]*AttestationManife
return attestationManifests, nil
}
// GetAttestationsFromImage extracts all attestation layers from an image
func GetAttestationsFromImage(image v1.Image) ([]*AttestationLayer, error) {
// GetAttestationsFromImage extracts all attestation layers from an image.
func GetAttestationsFromImage(image v1.Image) ([]*Layer, error) {
layers, err := image.Layers()
if err != nil {
return nil, fmt.Errorf("failed to extract layers from image: %w", err)
}
var attestationLayers []*AttestationLayer
var attestationLayers []*Layer
for _, layer := range layers {
// parse layer blob as json
r, err := layer.Uncompressed()
@@ -86,12 +88,12 @@ func GetAttestationsFromImage(image v1.Image) ([]*AttestationLayer, error) {
return nil, fmt.Errorf("failed to decode statement layer contents: %w", err)
}
}
attestationLayers = append(attestationLayers, &AttestationLayer{Layer: layer, Statement: stmt, Annotations: ann})
attestationLayers = append(attestationLayers, &Layer{Layer: layer, Statement: stmt, Annotations: ann})
}
return attestationLayers, nil
}
func (manifest *AttestationManifest) AddAttestation(ctx context.Context, signer dsse.SignerVerifier, statement *intoto.Statement, opts *SigningOptions) error {
func (manifest *Manifest) AddAttestation(ctx context.Context, signer dsse.SignerVerifier, statement *intoto.Statement, opts *SigningOptions) error {
layer, err := createSignedImageLayer(ctx, statement, signer, opts)
if err != nil {
return fmt.Errorf("failed to create signed layer: %w", err)
@@ -100,7 +102,7 @@ func (manifest *AttestationManifest) AddAttestation(ctx context.Context, signer
return nil
}
func createSignedImageLayer(ctx context.Context, statement *intoto.Statement, signer dsse.SignerVerifier, opts *SigningOptions) (*AttestationLayer, error) {
func createSignedImageLayer(ctx context.Context, statement *intoto.Statement, signer dsse.SignerVerifier, opts *SigningOptions) (*Layer, error) {
// sign the statement
env, err := SignInTotoStatement(ctx, statement, signer, opts)
if err != nil {
@@ -115,7 +117,7 @@ func createSignedImageLayer(ctx context.Context, statement *intoto.Statement, si
if err != nil {
return nil, fmt.Errorf("failed to marshal envelope: %w", err)
}
return &AttestationLayer{
return &Layer{
Statement: statement,
Annotations: map[string]string{
InTotoPredicateType: statement.PredicateType,
@@ -139,8 +141,8 @@ func SignInTotoStatement(ctx context.Context, statement *intoto.Statement, signe
func UpdateIndexImage(
idx v1.ImageIndex,
manifest *AttestationManifest,
options ...func(*AttestationManifestImageOptions) error,
manifest *Manifest,
options ...func(*ManifestImageOptions) error,
) (v1.ImageIndex, error) {
image, err := manifest.BuildAttestationImage(options...)
if err != nil {
@@ -164,7 +166,7 @@ func UpdateIndexImage(
return idx, nil
}
func UpdateIndexImages(idx v1.ImageIndex, manifest []*AttestationManifest, options ...func(*AttestationManifestImageOptions) error) (v1.ImageIndex, error) {
func UpdateIndexImages(idx v1.ImageIndex, manifest []*Manifest, options ...func(*ManifestImageOptions) error) (v1.ImageIndex, error) {
var err error
for _, m := range manifest {
idx, err = UpdateIndexImage(idx, m, options...)
@@ -175,8 +177,8 @@ func UpdateIndexImages(idx v1.ImageIndex, manifest []*AttestationManifest, optio
return idx, nil
}
func newOptions(options ...func(*AttestationManifestImageOptions) error) (*AttestationManifestImageOptions, error) {
opts := &AttestationManifestImageOptions{}
func newOptions(options ...func(*ManifestImageOptions) error) (*ManifestImageOptions, error) {
opts := &ManifestImageOptions{}
for _, opt := range options {
err := opt(opts)
if err != nil {
@@ -186,22 +188,22 @@ func newOptions(options ...func(*AttestationManifestImageOptions) error) (*Attes
return opts, nil
}
func WithoutSubject(skipSubject bool) func(*AttestationManifestImageOptions) error {
return func(r *AttestationManifestImageOptions) error {
func WithoutSubject(skipSubject bool) func(*ManifestImageOptions) error {
return func(r *ManifestImageOptions) error {
r.skipSubject = skipSubject
return nil
}
}
func WithReplacedLayers(replaceLayers bool) func(*AttestationManifestImageOptions) error {
return func(r *AttestationManifestImageOptions) error {
func WithReplacedLayers(replaceLayers bool) func(*ManifestImageOptions) error {
return func(r *ManifestImageOptions) error {
r.replaceLayers = replaceLayers
return nil
}
}
// build an image with signed attestations, optionally replacing existing layers with signed layers
func (manifest *AttestationManifest) BuildAttestationImage(options ...func(*AttestationManifestImageOptions) error) (v1.Image, error) {
// build an image with signed attestations, optionally replacing existing layers with signed layers.
func (manifest *Manifest) BuildAttestationImage(options ...func(*ManifestImageOptions) error) (v1.Image, error) {
opts, err := newOptions(options...)
if err != nil {
return nil, fmt.Errorf("failed to create options: %w", err)
@@ -224,7 +226,7 @@ func (manifest *AttestationManifest) BuildAttestationImage(options ...func(*Atte
resultLayers = append(resultLayers, existingLayer)
}
}
// so taht we attach all attestations to a single attestations image - as per current buildkit
// so that we attach all attestations to a single attestations image - as per current buildkit
opts.laxReferrers = true
newImg, err := buildImage(resultLayers, manifest.OriginalDescriptor, manifest.SubjectDescriptor, opts)
if err != nil {
@@ -233,12 +235,12 @@ func (manifest *AttestationManifest) BuildAttestationImage(options ...func(*Atte
return newImg, nil
}
// build an image per attestation (layer) suitable for use as Referrers
func (manifest *AttestationManifest) BuildReferringArtifacts() ([]v1.Image, error) {
// build an image per attestation (layer) suitable for use as Referrers.
func (manifest *Manifest) BuildReferringArtifacts() ([]v1.Image, error) {
var images []v1.Image
for _, layer := range manifest.SignedLayers {
opts := &AttestationManifestImageOptions{}
newImg, err := buildImage([]*AttestationLayer{layer}, manifest.OriginalDescriptor, manifest.SubjectDescriptor, opts)
opts := &ManifestImageOptions{}
newImg, err := buildImage([]*Layer{layer}, manifest.OriginalDescriptor, manifest.SubjectDescriptor, opts)
if err != nil {
return nil, fmt.Errorf("failed to build image: %w", err)
}
@@ -247,14 +249,14 @@ func (manifest *AttestationManifest) BuildReferringArtifacts() ([]v1.Image, erro
return images, nil
}
// build and image containing only layers
func buildImage(layers []*AttestationLayer, manifest *v1.Descriptor, subject *v1.Descriptor, opts *AttestationManifestImageOptions) (v1.Image, error) {
// build and image containing only layers.
func buildImage(layers []*Layer, manifest *v1.Descriptor, subject *v1.Descriptor, opts *ManifestImageOptions) (v1.Image, error) {
newImg := empty.Image
var err error
if len(layers) == 0 {
return nil, fmt.Errorf("no layers supplied to build image")
}
// NB: if we add the subject before the layers, it does not end up being computed/serialised in the output for some reason
// NB: if we add the subject before the layers, it does not end up being computed/serialized in the output for some reason
// TODO - recreate this bug and push upstream
for _, layer := range layers {
add := mutate.Addendum{
@@ -285,7 +287,11 @@ func buildImage(layers []*AttestationLayer, manifest *v1.Descriptor, subject *v1
// see note above - must be added after the layers!
if !opts.skipSubject {
subject.Platform = nil
newImg = mutate.Subject(newImg, *subject).(v1.Image)
ok := false
newImg, ok = mutate.Subject(newImg, *subject).(v1.Image)
if !ok {
return nil, fmt.Errorf("failed to set subject: %w", err)
}
}
if !opts.laxReferrers {
// as per https://github.com/opencontainers/image-spec/blob/main/manifest.md#guidance-for-an-empty-descriptor

View File

@@ -14,7 +14,7 @@ import (
"github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
)
func ExampleAttestationManifest() {
func ExampleManifest() {
// configure signerverifier
// local signer (unsafe for production)
signer, err := signerverifier.GenKeyPair()
@@ -55,7 +55,7 @@ func ExampleAttestationManifest() {
ID: "test-verifier",
},
TimeVerified: time.Now().UTC().Format(time.RFC3339),
ResourceUri: "some-uri",
ResourceURI: "some-uri",
Policy: attestation.VSAPolicy{URI: "some-uri"},
VerificationResult: "PASSED",
VerifiedLevels: []string{"SLSA_BUILD_LEVEL_1"},

View File

@@ -137,7 +137,7 @@ func TestAttestationReferenceTypes(t *testing.T) {
ref = fmt.Sprintf("%s/repo@%s", u.Host, idxDigest.String())
}
policyOpts := &policy.PolicyOptions{
policyOpts := &policy.Options{
LocalPolicyDir: LocalPolicy,
}
@@ -201,12 +201,12 @@ func TestReferencesInDifferentRepo(t *testing.T) {
} {
server := tc.server
defer server.Close()
serverUrl, err := url.Parse(server.URL)
serverURL, err := url.Parse(server.URL)
require.NoError(t, err)
refServer := tc.refServer
defer refServer.Close()
refServerUrl, err := url.Parse(refServer.URL)
refServerURL, err := url.Parse(refServer.URL)
require.NoError(t, err)
opts := &attestation.SigningOptions{
@@ -215,7 +215,7 @@ func TestReferencesInDifferentRepo(t *testing.T) {
attIdx, err := oci.IndexFromPath(UnsignedTestImage)
require.NoError(t, err)
indexName := fmt.Sprintf("%s/%s:latest", serverUrl.Host, repoName)
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
err = mirror.PushIndexToRegistry(attIdx.Index, indexName)
require.NoError(t, err)
@@ -227,12 +227,12 @@ func TestReferencesInDifferentRepo(t *testing.T) {
// push references using subject-digest.att convention
image, err := signedManifest.BuildAttestationImage()
require.NoError(t, err)
err = mirror.PushImageToRegistry(image, fmt.Sprintf("%s/%s:tag-does-not-matter", refServerUrl.Host, repoName))
err = mirror.PushImageToRegistry(image, fmt.Sprintf("%s/%s:tag-does-not-matter", refServerURL.Host, repoName))
require.NoError(t, err)
refServer := tc.refServer
defer refServer.Close()
refServerUrl, err := url.Parse(refServer.URL)
refServerURL, err := url.Parse(refServer.URL)
require.NoError(t, err)
opts := &attestation.SigningOptions{
@@ -241,7 +241,7 @@ func TestReferencesInDifferentRepo(t *testing.T) {
attIdx, err := oci.IndexFromPath(UnsignedTestImage)
require.NoError(t, err)
indexName := fmt.Sprintf("%s/%s:latest", serverUrl.Host, repoName)
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
err = mirror.PushIndexToRegistry(attIdx.Index, indexName)
require.NoError(t, err)
@@ -254,7 +254,7 @@ func TestReferencesInDifferentRepo(t *testing.T) {
imgs, err := mf.BuildReferringArtifacts()
require.NoError(t, err)
for _, img := range imgs {
err = mirror.PushImageToRegistry(img, fmt.Sprintf("%s/%s:tag-does-not-matter", refServerUrl.Host, repoName))
err = mirror.PushImageToRegistry(img, fmt.Sprintf("%s/%s:tag-does-not-matter", refServerURL.Host, repoName))
require.NoError(t, err)
}
}
@@ -267,7 +267,7 @@ func TestReferencesInDifferentRepo(t *testing.T) {
}
// can evaluate policy using referrers in a different repo
referencedImage := fmt.Sprintf("%s@%s", indexName, mf.Digest.String())
policyOpts := &policy.PolicyOptions{
policyOpts := &policy.Options{
LocalPolicyDir: PassPolicyDir,
}
src, err := oci.ParseImageSpec(referencedImage)
@@ -285,7 +285,7 @@ func TestCorrectArtifactTypeInTagFallback(t *testing.T) {
server := httptest.NewServer(registry.New())
defer server.Close()
serverUrl, err := url.Parse(server.URL)
serverURL, err := url.Parse(server.URL)
require.NoError(t, err)
repoName := "repo"
@@ -296,7 +296,7 @@ func TestCorrectArtifactTypeInTagFallback(t *testing.T) {
attIdx, err := oci.IndexFromPath(UnsignedTestImage)
require.NoError(t, err)
indexName := fmt.Sprintf("%s/%s:latest", serverUrl.Host, repoName)
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
err = mirror.PushIndexToRegistry(attIdx.Index, indexName)
require.NoError(t, err)
@@ -308,12 +308,12 @@ func TestCorrectArtifactTypeInTagFallback(t *testing.T) {
imgs, err := mf.BuildReferringArtifacts()
require.NoError(t, err)
for _, img := range imgs {
err = mirror.PushImageToRegistry(img, fmt.Sprintf("%s/%s:tag-does-not-matter", serverUrl.Host, repoName))
err = mirror.PushImageToRegistry(img, fmt.Sprintf("%s/%s:tag-does-not-matter", serverURL.Host, repoName))
require.NoError(t, err)
mf, err := img.Manifest()
require.NoError(t, err)
subject := mf.Subject
subjectRef, err := name.ParseReference(fmt.Sprintf("%s/%s:sha256-%s", serverUrl.Host, repoName, subject.Digest.Hex))
subjectRef, err := name.ParseReference(fmt.Sprintf("%s/%s:sha256-%s", serverURL.Host, repoName, subject.Digest.Hex))
require.NoError(t, err)
idx, err := remote.Index(subjectRef)
require.NoError(t, err)

View File

@@ -10,7 +10,7 @@ import (
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
// SignDSSE signs a payload with a given signer and uploads the signature to the transparency log
// SignDSSE signs a payload with a given signer and uploads the signature to the transparency log.
func SignDSSE(ctx context.Context, payload []byte, signer dsse.SignerVerifier, opts *SigningOptions) (*Envelope, error) {
payloadType := intoto.PayloadType
env := new(Envelope)
@@ -28,13 +28,13 @@ func SignDSSE(ctx context.Context, payload []byte, signer dsse.SignerVerifier, o
}
// get Key ID from signer
keyId, err := signer.KeyID()
keyID, err := signer.KeyID()
if err != nil {
return nil, fmt.Errorf("error getting public key ID: %w", err)
}
dsseSig := Signature{
KeyID: keyId,
dsseSig := &Signature{
KeyID: keyID,
Sig: base64Encoding.EncodeToString(sig),
}
if !opts.SkipTL {
@@ -42,22 +42,22 @@ func SignDSSE(ctx context.Context, payload []byte, signer dsse.SignerVerifier, o
if err != nil {
return nil, fmt.Errorf("failed to log to rekor: %w", err)
}
dsseSig.Extension = *ext
dsseSig.Extension = ext
}
// add signature to dsse envelope
env.Signatures = []Signature{dsseSig}
env.Signatures = []*Signature{dsseSig}
return env, nil
}
// returns a new envelope with the transparency log entry added to the signature extension
// returns a new envelope with the transparency log entry added to the signature extension.
func logSignature(ctx context.Context, t tlog.TL, sig *[]byte, encPayload *[]byte, signer dsse.SignerVerifier) (*Extension, error) {
// get Key ID from signer
keyId, err := signer.KeyID()
keyID, err := signer.KeyID()
if err != nil {
return nil, fmt.Errorf("error getting public key ID: %w", err)
}
entry, err := t.UploadLogEntry(ctx, keyId, *encPayload, *sig, signer)
entry, err := t.UploadLogEntry(ctx, keyID, *encPayload, *sig, signer)
if err != nil {
return nil, fmt.Errorf("error uploading TL entry: %w", err)
}
@@ -66,10 +66,10 @@ func logSignature(ctx context.Context, t tlog.TL, sig *[]byte, encPayload *[]byt
return nil, fmt.Errorf("error unmarshaling tl entry: %w", err)
}
return &Extension{
Kind: DockerDsseExtKind,
Ext: DockerDsseExtension{
Tl: DockerTlExtension{
Kind: RekorTlExtKind,
Kind: DockerDSSEExtKind,
Ext: &DockerDSSEExtension{
TL: &DockerTLExtension{
Kind: RekorTLExtKind,
Data: entryObj, // transparency log entry metadata
},
},

View File

@@ -44,20 +44,20 @@ func TestSignVerifyAttestation(t *testing.T) {
// signer.Public() used here for test purposes
ecPub, ok := signer.Public().(*ecdsa.PublicKey)
assert.True(t, ok)
pem, err := signerverifier.ToPEM(ecPub)
pem, err := signerverifier.ConvertToPEM(ecPub)
assert.NoError(t, err)
keyId, err := signerverifier.KeyID(ecPub)
keyID, err := signerverifier.KeyID(ecPub)
assert.NoError(t, err)
badKeyPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
badKey := &badKeyPriv.PublicKey
badPEM, err := signerverifier.ToPEM(badKey)
badPEM, err := signerverifier.ConvertToPEM(badKey)
require.NoError(t, err)
testCases := []struct {
name string
keyId string
keyID string
pem []byte
distrust bool
from time.Time
@@ -67,7 +67,7 @@ func TestSignVerifyAttestation(t *testing.T) {
}{
{
name: "all OK",
keyId: keyId,
keyID: keyID,
pem: pem,
distrust: false,
from: time.Time{},
@@ -77,17 +77,17 @@ func TestSignVerifyAttestation(t *testing.T) {
},
{
name: "key not found",
keyId: "someotherkey",
keyID: "someotherkey",
pem: pem,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: fmt.Sprintf("key not found: %s", keyId),
expectedError: fmt.Sprintf("key not found: %s", keyID),
},
{
name: "key distrusted",
keyId: keyId,
keyID: keyID,
pem: pem,
distrust: true,
from: time.Time{},
@@ -97,7 +97,7 @@ func TestSignVerifyAttestation(t *testing.T) {
},
{
name: "key not yet valid",
keyId: keyId,
keyID: keyID,
pem: pem,
distrust: false,
from: time.Now().Add(time.Hour),
@@ -107,7 +107,7 @@ func TestSignVerifyAttestation(t *testing.T) {
},
{
name: "key already revoked",
keyId: keyId,
keyID: keyID,
pem: pem,
distrust: false,
from: time.Time{},
@@ -117,7 +117,7 @@ func TestSignVerifyAttestation(t *testing.T) {
},
{
name: "bad key",
keyId: keyId,
keyID: keyID,
pem: badPEM,
distrust: false,
from: time.Time{},
@@ -129,8 +129,8 @@ func TestSignVerifyAttestation(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
keyMeta := attestation.KeyMetadata{
ID: tc.keyId,
keyMeta := &attestation.KeyMetadata{
ID: tc.keyID,
PEM: string(tc.pem),
Distrust: tc.distrust,
From: tc.from,

View File

@@ -15,8 +15,8 @@ const (
AttestationManifestType = "attestation-manifest"
InTotoPredicateType = "in-toto.io/predicate-type"
DockerReferenceDigest = "vnd.docker.reference.digest"
DockerDsseExtKind = "application/vnd.docker.attestation-verification.v1+json"
RekorTlExtKind = "Rekor"
DockerDSSEExtKind = "application/vnd.docker.attestation-verification.v1+json"
RekorTLExtKind = "Rekor"
OCIDescriptorDSSEMediaType = ociv1.MediaTypeDescriptor + "+dsse"
InTotoReferenceLifecycleStage = "vnd.docker.lifecycle-stage"
LifecycleStageExperimental = "experimental"
@@ -24,58 +24,58 @@ const (
var base64Encoding = base64.StdEncoding.Strict()
type AttestationLayer struct {
type Layer struct {
Statement *intoto.Statement
Layer v1.Layer
Annotations map[string]string
}
type AttestationManifest struct {
type Manifest struct {
OriginalDescriptor *v1.Descriptor
OriginalLayers []*AttestationLayer
OriginalLayers []*Layer
// accumulated during signing
SignedLayers []*AttestationLayer
SignedLayers []*Layer
// details of subect image
SubjectName string
SubjectDescriptor *v1.Descriptor
}
type AttestationManifestImageOptions struct {
type ManifestImageOptions struct {
// how to output the image
skipSubject bool
replaceLayers bool
laxReferrers bool
}
// the following types are needed until https://github.com/secure-systems-lab/dsse/pull/61 is merged
// the following types are needed until https://github.com/secure-systems-lab/dsse/pull/61 is merged.
type Envelope struct {
PayloadType string `json:"payloadType"`
Payload string `json:"payload"`
Signatures []Signature `json:"signatures"`
PayloadType string `json:"payloadType"`
Payload string `json:"payload"`
Signatures []*Signature `json:"signatures"`
}
type Signature struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
Extension Extension `json:"extension,omitempty"`
KeyID string `json:"keyid"`
Sig string `json:"sig"`
Extension *Extension `json:"extension,omitempty"`
}
type Extension struct {
Kind string `json:"kind"`
Ext DockerDsseExtension `json:"ext"`
Kind string `json:"kind"`
Ext *DockerDSSEExtension `json:"ext"`
}
type DockerDsseExtension struct {
Tl DockerTlExtension `json:"tl"`
type DockerDSSEExtension struct {
TL *DockerTLExtension `json:"tl"`
}
type DockerTlExtension struct {
type DockerTLExtension struct {
Kind string `json:"kind"`
Data any `json:"data"`
}
type VerifyOptions struct {
Keys []KeyMetadata `json:"keys"`
SkipTL bool `json:"skip_tl"`
Keys []*KeyMetadata `json:"keys"`
SkipTL bool `json:"skip_tl"`
}
type SigningOptions struct {

View File

@@ -28,8 +28,8 @@ type KeyMetadata struct {
}
type (
Keys []KeyMetadata
KeysMap map[string]KeyMetadata
Keys []*KeyMetadata
KeysMap map[string]*KeyMetadata
)
func VerifyDSSE(ctx context.Context, env *Envelope, opts *VerifyOptions) ([]byte, error) {
@@ -60,8 +60,8 @@ func VerifyDSSE(ctx context.Context, env *Envelope, opts *VerifyOptions) ([]byte
return payload, nil
}
func verifySignature(ctx context.Context, sig Signature, payload []byte, opts *VerifyOptions) error {
keys := make(map[string]KeyMetadata, len(opts.Keys))
func verifySignature(ctx context.Context, sig *Signature, payload []byte, opts *VerifyOptions) error {
keys := make(map[string]*KeyMetadata, len(opts.Keys))
for _, key := range opts.Keys {
keys[key.ID] = key
}
@@ -74,26 +74,25 @@ func verifySignature(ctx context.Context, sig Signature, payload []byte, opts *V
return fmt.Errorf("key %s is distrusted", keyMeta.ID)
}
// TODO: this is unmarshalling with MarshalPKIXPublicKey only for us to marshal it again
publicKey, err := signerverifier.Parse([]byte(keyMeta.PEM))
publicKey, err := signerverifier.ParsePublicKey([]byte(keyMeta.PEM))
if err != nil {
return fmt.Errorf("failed to parse public key: %w", err)
}
if !opts.SkipTL {
t := tlog.GetTL(ctx)
if sig.Extension.Kind == "" {
return fmt.Errorf("error missing signature extension kind")
if sig.Extension == nil || sig.Extension.Kind == "" {
return fmt.Errorf("error missing signature extension")
}
if sig.Extension.Kind != DockerDsseExtKind {
if sig.Extension.Kind != DockerDSSEExtKind {
return fmt.Errorf("error unsupported signature extension kind: %s", sig.Extension.Kind)
}
// verify TL entry
if sig.Extension.Ext.Tl.Kind != RekorTlExtKind {
return fmt.Errorf("error unsupported TL extension kind: %s", sig.Extension.Ext.Tl.Kind)
if sig.Extension.Ext.TL.Kind != RekorTLExtKind {
return fmt.Errorf("error unsupported TL extension kind: %s", sig.Extension.Ext.TL.Kind)
}
entry := sig.Extension.Ext.Tl.Data
entry := sig.Extension.Ext.TL.Data
entryBytes, err := json.Marshal(entry)
if err != nil {
return fmt.Errorf("failed to marshal TL entry: %w", err)

View File

@@ -35,7 +35,7 @@ func TestVerifyUnsignedAttestation(t *testing.T) {
payload := []byte("payload")
env := &attestation.Envelope{
// no signatures
Signatures: []attestation.Signature{},
Signatures: []*attestation.Signature{},
Payload: base64.StdEncoding.EncodeToString(payload),
PayloadType: intoto.PayloadType,
}

View File

@@ -14,7 +14,7 @@ const (
type VSAPredicate struct {
Verifier VSAVerifier `json:"verifier"`
TimeVerified string `json:"timeVerified"`
ResourceUri string `json:"resourceUri"`
ResourceURI string `json:"resourceUri"`
Policy VSAPolicy `json:"policy"`
InputAttestations []VSAInputAttestation `json:"inputAttestations"`
VerificationResult string `json:"verificationResult"`

View File

@@ -31,7 +31,7 @@ func LoadLocalMappings(configDir string) (*PolicyMappings, error) {
return expandMappingFile(mappings)
}
func LoadTufMappings(tufClient tuf.TUFClient, localTargetsDir string) (*PolicyMappings, error) {
func LoadTUFMappings(tufClient tuf.Downloader, localTargetsDir string) (*PolicyMappings, error) {
if tufClient == nil {
return nil, fmt.Errorf("tuf client not set")
}
@@ -52,7 +52,7 @@ func LoadTufMappings(tufClient tuf.TUFClient, localTargetsDir string) (*PolicyMa
func expandMappingFile(mappingFile *policyMappingsFile) (*PolicyMappings, error) {
policies := make(map[string]*PolicyMapping)
for _, policy := range mappingFile.Policies {
policies[policy.Id] = policy
policies[policy.ID] = policy
}
var rules []*PolicyRule
@@ -63,7 +63,7 @@ func expandMappingFile(mappingFile *policyMappingsFile) (*PolicyMappings, error)
}
rules = append(rules, &PolicyRule{
Pattern: r,
PolicyId: rule.PolicyId,
PolicyID: rule.PolicyID,
Replacement: rule.Replacement,
})
}

View File

@@ -13,7 +13,7 @@ type policyMappingsFile struct {
type policyRuleFile struct {
Pattern string `json:"pattern"`
PolicyId string `json:"policy-id"`
PolicyID string `json:"policy-id"`
Replacement string `json:"rewrite"`
}
@@ -32,7 +32,7 @@ const (
)
type PolicyMapping struct {
Id string `json:"id"`
ID string `json:"id"`
Description string `json:"description"`
Files []PolicyMappingFile `json:"files"`
Attestations *AttestationConfig `json:"attestations"`
@@ -49,6 +49,6 @@ type PolicyMappingFile struct {
type PolicyRule struct {
Pattern *regexp.Regexp
PolicyId string
PolicyID string
Replacement string
}

View File

@@ -14,12 +14,12 @@ import (
type TufMirrorOutput struct {
metadata v1.Image
delegatedMetadata []*mirror.MirrorImage
targets []*mirror.MirrorImage
delegatedTargets []*mirror.MirrorIndex
delegatedMetadata []*mirror.Image
targets []*mirror.Image
delegatedTargets []*mirror.Index
}
func ExampleNewTufMirror() {
func ExampleNewTUFMirror() {
home, err := os.UserHomeDir()
if err != nil {
panic(err)
@@ -29,7 +29,7 @@ func ExampleNewTufMirror() {
// configure TUF mirror
metadataURI := "https://docker.github.io/tuf-staging/metadata"
targetsURI := "https://docker.github.io/tuf-staging/targets"
m, err := mirror.NewTufMirror(embed.RootStaging.Data, tufOutputPath, metadataURI, targetsURI, tuf.NewMockVersionChecker())
m, err := mirror.NewTUFMirror(embed.RootStaging.Data, tufOutputPath, metadataURI, targetsURI, tuf.NewMockVersionChecker())
if err != nil {
panic(err)
}
@@ -46,7 +46,7 @@ func ExampleNewTufMirror() {
}
// create targets manifest
targets, err := m.GetTufTargetMirrors()
targets, err := m.GetTUFTargetMirrors()
if err != nil {
panic(err)
}

View File

@@ -16,9 +16,9 @@ import (
// TUF root metadata
// -----------------
// GetMetadataManifest returns an image with TUF root metadata as layers
func (m *TufMirror) GetMetadataManifest(metadataURL string) (v1.Image, error) {
metadata, err := m.getTufMetadataMirror(metadataURL)
// GetMetadataManifest returns an image with TUF root metadata as layers.
func (m *TUFMirror) GetMetadataManifest(metadataURL string) (v1.Image, error) {
metadata, err := m.getMetadataMirror(metadataURL)
if err != nil {
return nil, fmt.Errorf("failed to get metadata: %w", err)
}
@@ -29,16 +29,16 @@ func (m *TufMirror) GetMetadataManifest(metadataURL string) (v1.Image, error) {
return manifest, nil
}
// getTufMetadataMirror returns a TufMetadata struct with TUF metadata as map of file names to bytes
func (m *TufMirror) getTufMetadataMirror(metadataURL string) (*TufMetadata, error) {
trustedMetadata := m.TufClient.GetMetadata()
// getMetadataMirror returns a TufMetadata struct with TUF metadata as map of file names to bytes.
func (m *TUFMirror) getMetadataMirror(metadataURL string) (*TUFMetadata, error) {
trustedMetadata := m.TUFClient.GetMetadata()
rootMetadata := map[string][]byte{}
rootVersion := trustedMetadata.Root.Signed.Version
// get the previous versions of root metadata if any
if rootVersion != 1 {
var err error
rootMetadata, err = m.TufClient.GetPriorRoots(metadataURL)
rootMetadata, err = m.TUFClient.GetPriorRoots(metadataURL)
if err != nil {
return nil, fmt.Errorf("failed to get prior root metadata: %w", err)
}
@@ -69,7 +69,7 @@ func (m *TufMirror) getTufMetadataMirror(metadataURL string) (*TufMetadata, erro
snapshotVersion = strconv.FormatInt(trustedMetadata.Snapshot.Signed.Version, 10)
targetsVersion = strconv.FormatInt(trustedMetadata.Targets[metadata.TARGETS].Signed.Version, 10)
}
return &TufMetadata{
return &TUFMetadata{
Root: rootMetadata,
Snapshot: map[string][]byte{nameFromRole(metadata.SNAPSHOT, snapshotVersion): snapshotBytes},
Targets: map[string][]byte{nameFromRole(metadata.TARGETS, targetsVersion): targetsBytes},
@@ -77,12 +77,12 @@ func (m *TufMirror) getTufMetadataMirror(metadataURL string) (*TufMetadata, erro
}, nil
}
// buildMetadataManifest returns an OCI image with TUF metadata as layers with annotations
func (m *TufMirror) buildMetadataManifest(metadata *TufMetadata) (v1.Image, error) {
// buildMetadataManifest returns an OCI image with TUF metadata as layers with annotations.
func (m *TUFMirror) buildMetadataManifest(metadata *TUFMetadata) (v1.Image, error) {
img := empty.Image
img = mutate.MediaType(img, types.OCIManifestSchema1)
img = mutate.ConfigMediaType(img, types.OCIConfigJSON)
for _, role := range TufRoles {
for _, role := range TUFRoles {
layers, err := m.makeRoleLayers(role, metadata)
if err != nil {
return nil, fmt.Errorf("failed to make role layer: %w", err)
@@ -95,8 +95,8 @@ func (m *TufMirror) buildMetadataManifest(metadata *TufMetadata) (v1.Image, erro
return img, nil
}
// makeRoleLayers returns a list of layers for a given TUF role
func (m *TufMirror) makeRoleLayers(role TufRole, tufMetadata *TufMetadata) ([]mutate.Addendum, error) {
// makeRoleLayers returns a list of layers for a given TUF role.
func (m *TUFMirror) makeRoleLayers(role TUFRole, tufMetadata *TUFMetadata) ([]mutate.Addendum, error) {
var layers []mutate.Addendum
ann := map[string]string{tufFileAnnotation: ""}
switch role {
@@ -115,8 +115,8 @@ func (m *TufMirror) makeRoleLayers(role TufRole, tufMetadata *TufMetadata) ([]mu
return layers, nil
}
// annotatedMetaLayers returns a list of layers with annotations for each TUF metadata file
func (m *TufMirror) annotatedMetaLayers(meta map[string][]byte) []mutate.Addendum {
// annotatedMetaLayers returns a list of layers with annotations for each TUF metadata file.
func (m *TUFMirror) annotatedMetaLayers(meta map[string][]byte) []mutate.Addendum {
var layers []mutate.Addendum
for name, data := range meta {
ann := map[string]string{tufFileAnnotation: name}
@@ -129,8 +129,8 @@ func (m *TufMirror) annotatedMetaLayers(meta map[string][]byte) []mutate.Addendu
// TUF delegated targets metadata
// ------------------------------
// GetDelegatedMetadataMirrors returns a list of mirrors (image/tag pairs) for each delegated targets role metadata
func (m *TufMirror) GetDelegatedMetadataMirrors() ([]*MirrorImage, error) {
// GetDelegatedMetadataMirrors returns a list of mirrors (image/tag pairs) for each delegated targets role metadata.
func (m *TUFMirror) GetDelegatedMetadataMirrors() ([]*Image, error) {
// get current delegated targets metadata
delegatedTargets, err := m.getDelegatedTargetsMetadata()
if err != nil {
@@ -143,12 +143,12 @@ func (m *TufMirror) GetDelegatedMetadataMirrors() ([]*MirrorImage, error) {
return mirror, nil
}
// getDelegatedTargetsMetadata returns delegated targets metadata as a list of DelegatedTargetMetadata (role name and data)
func (m *TufMirror) getDelegatedTargetsMetadata() ([]DelegatedTargetMetadata, error) {
// getDelegatedTargetsMetadata returns delegated targets metadata as a list of DelegatedTargetMetadata (role name and data).
func (m *TUFMirror) getDelegatedTargetsMetadata() ([]DelegatedTargetMetadata, error) {
var delegatedTargets []DelegatedTargetMetadata
md := m.TufClient.GetMetadata()
md := m.TUFClient.GetMetadata()
for _, role := range md.Targets[metadata.TARGETS].Signed.Delegations.Roles {
roleMetadata, err := m.TufClient.LoadDelegatedTargets(role.Name, metadata.TARGETS)
roleMetadata, err := m.TUFClient.LoadDelegatedTargets(role.Name, metadata.TARGETS)
if err != nil {
return nil, fmt.Errorf("failed to get delegated role metadata: %w", err)
}
@@ -170,9 +170,9 @@ func (m *TufMirror) getDelegatedTargetsMetadata() ([]DelegatedTargetMetadata, er
return delegatedTargets, nil
}
// buildDelegatedMetadataManifests returns a list of mirrors (image/tag pairs) for each delegated target role metadata
func (m *TufMirror) buildDelegatedMetadataManifests(delegated []DelegatedTargetMetadata) ([]*MirrorImage, error) {
manifests := []*MirrorImage{}
// buildDelegatedMetadataManifests returns a list of mirrors (image/tag pairs) for each delegated target role metadata.
func (m *TUFMirror) buildDelegatedMetadataManifests(delegated []DelegatedTargetMetadata) ([]*Image, error) {
manifests := []*Image{}
for _, role := range delegated {
img := empty.Image
img = mutate.MediaType(img, types.OCIManifestSchema1)
@@ -183,7 +183,7 @@ func (m *TufMirror) buildDelegatedMetadataManifests(delegated []DelegatedTargetM
if err != nil {
return nil, fmt.Errorf("failed to append delegated targets layer to image: %w", err)
}
manifests = append(manifests, &MirrorImage{Image: img, Tag: role.Name})
manifests = append(manifests, &Image{Image: img, Tag: role.Name})
}
return manifests, nil
}

View File

@@ -21,10 +21,10 @@ func TestGetTufMetadataMirror(t *testing.T) {
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
m, err := NewTUFMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
assert.NoError(t, err)
tufMetadata, err := m.getTufMetadataMirror(server.URL + "/metadata")
tufMetadata, err := m.getMetadataMirror(server.URL + "/metadata")
assert.NoError(t, err)
// check that all roles are not empty
@@ -39,7 +39,7 @@ func TestGetMetadataManifest(t *testing.T) {
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
m, err := NewTUFMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
assert.NoError(t, err)
img, err := m.GetMetadataManifest(server.URL + "/metadata")
@@ -78,7 +78,7 @@ func TestGetDelegatedMetadataMirrors(t *testing.T) {
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
m, err := NewTUFMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
assert.NoError(t, err)
delegations, err := m.GetDelegatedMetadataMirrors()

View File

@@ -16,15 +16,15 @@ import (
"github.com/google/go-containerregistry/pkg/v1/remote"
)
func NewTufMirror(root []byte, tufPath, metadataURL, targetsURL string, versionChecker tuf.VersionChecker) (*TufMirror, error) {
func NewTUFMirror(root []byte, tufPath, metadataURL, targetsURL string, versionChecker tuf.VersionChecker) (*TUFMirror, error) {
if root == nil {
root = embed.RootDefault.Data
}
tufClient, err := tuf.NewTufClient(root, tufPath, metadataURL, targetsURL, versionChecker)
tufClient, err := tuf.NewClient(root, tufPath, metadataURL, targetsURL, versionChecker)
if err != nil {
return nil, fmt.Errorf("failed to create TUF client: %w", err)
}
return &TufMirror{TufClient: tufClient, tufPath: tufPath, metadataURL: metadataURL, targetsURL: targetsURL}, nil
return &TUFMirror{TUFClient: tufClient, tufPath: tufPath, metadataURL: metadataURL, targetsURL: targetsURL}, nil
}
func PushImageToRegistry(image v1.Image, imageName string) error {
@@ -85,7 +85,7 @@ func SaveIndex(outputs []*oci.ImageSpec, index v1.ImageIndex, indexName string)
Add: index,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OciReferenceTarget: indexName,
oci.OCIReferenceTarget: indexName,
},
},
})
@@ -110,7 +110,7 @@ func SaveImage(output *oci.ImageSpec, image v1.Image, imageName string) error {
Add: image,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OciReferenceTarget: imageName,
oci.OCIReferenceTarget: imageName,
},
},
})
@@ -127,7 +127,7 @@ func SaveImage(output *oci.ImageSpec, image v1.Image, imageName string) error {
return nil
}
func SaveReferrers(manifest *attestation.AttestationManifest, outputs []*oci.ImageSpec) error {
func SaveReferrers(manifest *attestation.Manifest, outputs []*oci.ImageSpec) error {
for _, output := range outputs {
if output.Type == oci.OCI {
continue

View File

@@ -13,16 +13,16 @@ import (
"github.com/theupdateframework/go-tuf/v2/metadata"
)
// GetTufTargetMirrors returns a list of top-level target files as MirrorImages (image with tag)
func (m *TufMirror) GetTufTargetMirrors() ([]*MirrorImage, error) {
targetMirrors := []*MirrorImage{}
md := m.TufClient.GetMetadata()
// GetTUFTargetMirrors returns a list of top-level target files as MirrorImages (image with tag).
func (m *TUFMirror) GetTUFTargetMirrors() ([]*Image, error) {
targetMirrors := []*Image{}
md := m.TUFClient.GetMetadata()
// for each top-level target file, create an image with the target file as a layer
targets := md.Targets[metadata.TARGETS].Signed.Targets
for _, t := range targets {
// download target file
_, data, err := m.TufClient.DownloadTarget(t.Path, filepath.Join(m.tufPath, "download"))
_, data, err := m.TUFClient.DownloadTarget(t.Path, filepath.Join(m.tufPath, "download"))
if err != nil {
return nil, fmt.Errorf("failed to download target %s: %w", t.Path, err)
}
@@ -42,16 +42,16 @@ func (m *TufMirror) GetTufTargetMirrors() ([]*MirrorImage, error) {
if err != nil {
return nil, fmt.Errorf("failed to append role layer to image: %w", err)
}
targetMirrors = append(targetMirrors, &MirrorImage{Image: img, Tag: name})
targetMirrors = append(targetMirrors, &Image{Image: img, Tag: name})
}
return targetMirrors, nil
}
// GetDelegatedTargetMirrors returns a list of delegated target files as MirrorIndexes (image index with tag)
// each image in the index contains a delegated target file
func (m *TufMirror) GetDelegatedTargetMirrors() ([]*MirrorIndex, error) {
mirror := []*MirrorIndex{}
md := m.TufClient.GetMetadata()
// each image in the index contains a delegated target file.
func (m *TUFMirror) GetDelegatedTargetMirrors() ([]*Index, error) {
mirror := []*Index{}
md := m.TUFClient.GetMetadata()
// for each delegated role, create an image index with target files as images
roles := md.Targets[metadata.TARGETS].Signed.Delegations.Roles
@@ -60,7 +60,7 @@ func (m *TufMirror) GetDelegatedTargetMirrors() ([]*MirrorIndex, error) {
index := v1.ImageIndex(empty.Index)
// get delegated targets metadata for role
roleMeta, err := m.TufClient.LoadDelegatedTargets(role.Name, metadata.TARGETS)
roleMeta, err := m.TUFClient.LoadDelegatedTargets(role.Name, metadata.TARGETS)
if err != nil {
return nil, fmt.Errorf("failed to load delegated targets metadata: %w", err)
}
@@ -68,7 +68,7 @@ func (m *TufMirror) GetDelegatedTargetMirrors() ([]*MirrorIndex, error) {
// for each target file, create an image with the target file as a layer
for _, target := range roleMeta.Signed.Targets {
// download target file
_, data, err := m.TufClient.DownloadTarget(target.Path, filepath.Join(m.tufPath, "download"))
_, data, err := m.TUFClient.DownloadTarget(target.Path, filepath.Join(m.tufPath, "download"))
if err != nil {
return nil, fmt.Errorf("failed to download target %s: %w", target.Path, err)
}
@@ -103,7 +103,7 @@ func (m *TufMirror) GetDelegatedTargetMirrors() ([]*MirrorIndex, error) {
},
})
}
mirror = append(mirror, &MirrorIndex{Index: index, Tag: role.Name})
mirror = append(mirror, &Index{Index: index, Tag: role.Name})
}
return mirror, nil
}

View File

@@ -27,10 +27,10 @@ func TestGetTufTargetsMirror(t *testing.T) {
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
m, err := NewTUFMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
assert.NoError(t, err)
targets, err := m.GetTufTargetMirrors()
targets, err := m.GetTUFTargetMirrors()
assert.NoError(t, err)
assert.Greater(t, len(targets), 0)
@@ -61,10 +61,10 @@ func TestTargetDelegationMetadata(t *testing.T) {
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
tm, err := NewTufMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
tm, err := NewTUFMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
assert.NoError(t, err)
targets, err := tm.TufClient.LoadDelegatedTargets("test-role", "targets")
targets, err := tm.TUFClient.LoadDelegatedTargets("test-role", "targets")
assert.NoError(t, err)
assert.Greater(t, len(targets.Signed.Targets), 0)
}
@@ -74,7 +74,7 @@ func TestGetDelegatedTargetMirrors(t *testing.T) {
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
m, err := NewTUFMirror(embed.RootDev.Data, path, server.URL+"/metadata", server.URL+"/targets", tuf.NewMockVersionChecker())
assert.NoError(t, err)
mirrors, err := m.GetDelegatedTargetMirrors()

View File

@@ -14,11 +14,11 @@ const (
tufFileAnnotation = "tuf.io/filename"
)
type TufRole string
type TUFRole string
var TufRoles = []TufRole{metadata.ROOT, metadata.SNAPSHOT, metadata.TARGETS, metadata.TIMESTAMP}
var TUFRoles = []TUFRole{metadata.ROOT, metadata.SNAPSHOT, metadata.TARGETS, metadata.TIMESTAMP}
type TufMetadata struct {
type TUFMetadata struct {
Root map[string][]byte
Snapshot map[string][]byte
Targets map[string][]byte
@@ -31,18 +31,18 @@ type DelegatedTargetMetadata struct {
Data []byte
}
type MirrorImage struct {
type Image struct {
Image v1.Image
Tag string
}
type MirrorIndex struct {
type Index struct {
Index v1.ImageIndex
Tag string
}
type TufMirror struct {
TufClient *tuf.TufClient
type TUFMirror struct {
TUFClient *tuf.Client
tufPath string
metadataURL string
targetsURL string

View File

@@ -7,21 +7,21 @@ import (
)
type userAgentTransporter struct {
ua string
rt http.RoundTripper
userAgent string
roundTripper http.RoundTripper
}
type Option = func(*http.Client)
func (u *userAgentTransporter) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("User-Agent", u.ua)
req.Header.Set("User-Agent", u.userAgent)
return u.rt.RoundTrip(req)
return u.roundTripper.RoundTrip(req)
}
func HttpTransport() http.RoundTripper {
func HTTPTransport() http.RoundTripper {
return &userAgentTransporter{
ua: "Docker-Client",
rt: cleanhttp.DefaultTransport(),
userAgent: "Docker-Client",
roundTripper: cleanhttp.DefaultTransport(),
}
}

View File

@@ -11,14 +11,14 @@ import (
"github.com/google/go-containerregistry/pkg/v1/layout"
)
// implementation of AttestationResolver that closes over attestations from an oci layout
type OCILayoutResolver struct {
*attestation.AttestationManifest
// implementation of AttestationResolver that closes over attestations from an oci layout.
type LayoutResolver struct {
*attestation.Manifest
*ImageSpec
}
func NewOCILayoutAttestationResolver(src *ImageSpec) (*OCILayoutResolver, error) {
r := &OCILayoutResolver{
func NewOCILayoutAttestationResolver(src *ImageSpec) (*LayoutResolver, error) {
r := &LayoutResolver{
ImageSpec: src,
}
_, err := r.fetchAttestationManifest()
@@ -28,25 +28,25 @@ func NewOCILayoutAttestationResolver(src *ImageSpec) (*OCILayoutResolver, error)
return r, nil
}
func (r *OCILayoutResolver) fetchAttestationManifest() (*attestation.AttestationManifest, error) {
if r.AttestationManifest == nil {
func (r *LayoutResolver) fetchAttestationManifest() (*attestation.Manifest, error) {
if r.Manifest == nil {
m, err := attestationManifestFromOCILayout(r.Identifier, r.ImageSpec.Platform)
if err != nil {
return nil, err
}
r.AttestationManifest = m
r.Manifest = m
}
return r.AttestationManifest, nil
return r.Manifest, nil
}
func (r *OCILayoutResolver) Attestations(ctx context.Context, predicateType string) ([]*att.Envelope, error) {
func (r *LayoutResolver) Attestations(_ context.Context, predicateType string) ([]*att.Envelope, error) {
var envs []*att.Envelope
dsseMediaType, err := attestation.DSSEMediaType(predicateType)
if err != nil {
return nil, fmt.Errorf("failed to get DSSE media type for predicate '%s': %w", predicateType, err)
}
for _, attestationLayer := range r.AttestationManifest.OriginalLayers {
for _, attestationLayer := range r.Manifest.OriginalLayers {
mt, err := attestationLayer.Layer.MediaType()
if err != nil {
return nil, fmt.Errorf("failed to get layer media type: %w", err)
@@ -71,19 +71,19 @@ func (r *OCILayoutResolver) Attestations(ctx context.Context, predicateType stri
return envs, nil
}
func (r *OCILayoutResolver) ImageName(ctx context.Context) (string, error) {
func (r *LayoutResolver) ImageName(_ context.Context) (string, error) {
return r.SubjectName, nil
}
func (r *OCILayoutResolver) ImageDescriptor(ctx context.Context) (*v1.Descriptor, error) {
func (r *LayoutResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error) {
return r.SubjectDescriptor, nil
}
func (r *OCILayoutResolver) ImagePlatform(ctx context.Context) (*v1.Platform, error) {
func (r *LayoutResolver) ImagePlatform(_ context.Context) (*v1.Platform, error) {
return r.ImageSpec.Platform, nil
}
func attestationManifestFromOCILayout(path string, platform *v1.Platform) (*attestation.AttestationManifest, error) {
func attestationManifestFromOCILayout(path string, platform *v1.Platform) (*attestation.Manifest, error) {
idx, err := layout.ImageIndexFromPath(path)
if err != nil {
return nil, err
@@ -107,13 +107,15 @@ func attestationManifestFromOCILayout(path string, platform *v1.Platform) (*atte
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
}
var subjectDescriptor *v1.Descriptor
for _, mf := range mfs2.Manifests {
if mf.Platform.Equals(*platform) {
subjectDescriptor = &mf
for i := range mfs2.Manifests {
manifest := &mfs2.Manifests[i]
if manifest.Platform.Equals(*platform) {
subjectDescriptor = manifest
break
}
}
for _, mf := range mfs2.Manifests {
for i := range mfs2.Manifests {
mf := &mfs2.Manifests[i]
if mf.Annotations[att.DockerReferenceType] != attestation.AttestationManifestType {
continue
}
@@ -130,9 +132,9 @@ func attestationManifestFromOCILayout(path string, platform *v1.Platform) (*atte
if err != nil {
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
}
attest := &attestation.AttestationManifest{
attest := &attestation.Manifest{
OriginalLayers: layers,
OriginalDescriptor: &mf,
OriginalDescriptor: mf,
SubjectName: name,
SubjectDescriptor: subjectDescriptor,
}

View File

@@ -18,7 +18,7 @@ import (
)
// ParsePlatform parses the provided platform string or attempts to obtain
// the platform of the current host system
// the platform of the current host system.
func ParsePlatform(platformStr string) (*v1.Platform, error) {
if platformStr == "" {
cdp := platforms.Normalize(platforms.DefaultSpec())
@@ -30,14 +30,13 @@ func ParsePlatform(platformStr string) (*v1.Platform, error) {
Architecture: cdp.Architecture,
Variant: cdp.Variant,
}, nil
} else {
return v1.ParsePlatform(platformStr)
}
return v1.ParsePlatform(platformStr)
}
func WithOptions(ctx context.Context, platform *v1.Platform) []remote.Option {
// prepare options
options := []remote.Option{MultiKeychainOption(), remote.WithTransport(HttpTransport()), remote.WithContext(ctx)}
options := []remote.Option{MultiKeychainOption(), remote.WithTransport(HTTPTransport()), remote.WithContext(ctx)}
// add in platform into remote Get operation; this might conflict with an explicit digest, but we are trying anyway
if platform != nil {
@@ -46,7 +45,7 @@ func WithOptions(ctx context.Context, platform *v1.Platform) []remote.Option {
return options
}
func ExtractEnvelopes(manifest *attestation.AttestationManifest, predicateType string) ([]*att.Envelope, error) {
func ExtractEnvelopes(manifest *attestation.Manifest, predicateType string) ([]*att.Envelope, error) {
var envs []*att.Envelope
dsseMediaType, err := attestation.DSSEMediaType(predicateType)
if err != nil {
@@ -76,16 +75,18 @@ func ExtractEnvelopes(manifest *attestation.AttestationManifest, predicateType s
}
func imageDescriptor(ix *v1.IndexManifest, platform *v1.Platform) (*v1.Descriptor, error) {
for _, m := range ix.Manifests {
for i := range ix.Manifests {
m := &ix.Manifests[i]
if (m.MediaType == ocispec.MediaTypeImageManifest || m.MediaType == "application/vnd.docker.distribution.manifest.v2+json") && m.Platform.Equals(*platform) {
return &m, nil
return m, nil
}
}
return nil, fmt.Errorf("no image found for platform %v", platform)
}
func attestationDigestForDigest(ix *v1.IndexManifest, imageDigest string, attestType string) (string, error) {
for _, m := range ix.Manifests {
for i := range ix.Manifests {
m := &ix.Manifests[i]
if v, ok := m.Annotations[att.DockerReferenceType]; ok && v == attestType {
if d, ok := m.Annotations[att.DockerReferenceDigest]; ok && d == imageDigest {
return m.Digest.String(), nil
@@ -160,7 +161,7 @@ func ReplaceTagInSpec(src *ImageSpec, digest v1.Hash) (*ImageSpec, error) {
}, nil
}
// so that the index tag is replaced with a tag unique to the image digest and doesn't overwrite it
// so that the index tag is replaced with a tag unique to the image digest and doesn't overwrite it.
func replaceTag(image string, digest v1.Hash) (string, error) {
if strings.HasPrefix(image, LocalPrefix) {
return image, nil

View File

@@ -35,7 +35,7 @@ func WithReferrersRepo(repo string) func(*ReferrersResolver) error {
}
}
func (r *ReferrersResolver) resolveAttestations(ctx context.Context, predicateType string) ([]*attestation.AttestationManifest, error) {
func (r *ReferrersResolver) resolveAttestations(ctx context.Context, predicateType string) ([]*attestation.Manifest, error) {
dsseMediaType, err := attestation.DSSEMediaType(predicateType)
if err != nil {
return nil, fmt.Errorf("failed to get DSSE media type for predicate '%s': %w", predicateType, err)
@@ -75,8 +75,9 @@ func (r *ReferrersResolver) resolveAttestations(ctx context.Context, predicateTy
if err != nil {
return nil, fmt.Errorf("failed to get index manifest: %w", err)
}
aManifests := make([]*attestation.AttestationManifest, 0)
for _, m := range referrersIndexManifest.Manifests {
aManifests := make([]*attestation.Manifest, 0)
for i := range referrersIndexManifest.Manifests {
m := referrersIndexManifest.Manifests[i]
remoteRef := referrersSubjectRef.Context().Digest(m.Digest.String())
options = WithOptions(ctx, nil)
attestationImage, err := remote.Image(remoteRef, options...)
@@ -97,7 +98,7 @@ func (r *ReferrersResolver) resolveAttestations(ctx context.Context, predicateTy
if string(mt) != dsseMediaType {
return nil, fmt.Errorf("expected layer media type %s, got %s", dsseMediaType, mt)
}
attest := &attestation.AttestationManifest{
attest := &attestation.Manifest{
SubjectName: imageName,
OriginalLayers: layers,
OriginalDescriptor: &m,

View File

@@ -13,7 +13,7 @@ import (
type RegistryResolver struct {
*RegistryImageDetailsResolver
*attestation.AttestationManifest
*attestation.Manifest
}
type RegistryImageDetailsResolver struct {
@@ -33,11 +33,11 @@ func NewRegistryAttestationResolver(src *RegistryImageDetailsResolver) (*Registr
}, nil
}
func (r *RegistryImageDetailsResolver) ImageName(ctx context.Context) (string, error) {
func (r *RegistryImageDetailsResolver) ImageName(_ context.Context) (string, error) {
return r.Identifier, nil
}
func (r *RegistryImageDetailsResolver) ImagePlatform(ctx context.Context) (*v1.Platform, error) {
func (r *RegistryImageDetailsResolver) ImagePlatform(_ context.Context) (*v1.Platform, error) {
return r.Platform, nil
}
@@ -74,17 +74,17 @@ func (r *RegistryImageDetailsResolver) ImageDescriptor(ctx context.Context) (*v1
}
func (r *RegistryResolver) Attestations(ctx context.Context, predicateType string) ([]*att.Envelope, error) {
if r.AttestationManifest == nil {
if r.Manifest == nil {
attest, err := FetchAttestationManifest(ctx, r.Identifier, r.ImageSpec.Platform)
if err != nil {
return nil, err
}
r.AttestationManifest = attest
r.Manifest = attest
}
return ExtractEnvelopes(r.AttestationManifest, predicateType)
return ExtractEnvelopes(r.Manifest, predicateType)
}
func FetchAttestationManifest(ctx context.Context, image string, platform *v1.Platform) (*attestation.AttestationManifest, error) {
func FetchAttestationManifest(ctx context.Context, image string, platform *v1.Platform) (*attestation.Manifest, error) {
// we want to get to the image index, so ignoring platform for now
options := WithOptions(ctx, nil)
ref, err := name.ParseReference(image)
@@ -131,7 +131,7 @@ func FetchAttestationManifest(ctx context.Context, image string, platform *v1.Pl
if err != nil {
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
}
attest := &attestation.AttestationManifest{
attest := &attestation.Manifest{
OriginalLayers: layers,
OriginalDescriptor: &remoteDescriptor.Descriptor,
SubjectName: image,

View File

@@ -11,7 +11,7 @@ import (
)
const (
OciReferenceTarget = "org.opencontainers.image.ref.name"
OCIReferenceTarget = "org.opencontainers.image.ref.name"
LocalPrefix = "oci://"
RegistryPrefix = "docker://"
OCI SourceType = "OCI"
@@ -52,7 +52,7 @@ func IndexFromPath(path string) (*NamedIndex, error) {
if err != nil {
return nil, fmt.Errorf("failed to get digest: %w", err)
}
imageName := idxm.Manifests[0].Annotations[OciReferenceTarget]
imageName := idxm.Manifests[0].Annotations[OCIReferenceTarget]
idxDigest := idxm.Manifests[0].Digest
idx, err := wrapperIdx.ImageIndex(idxDigest)
@@ -85,9 +85,8 @@ func IndexFromRemote(image string) (*NamedIndex, error) {
func LoadIndex(input *ImageSpec) (*NamedIndex, error) {
if input.Type == OCI {
return IndexFromPath(input.Identifier)
} else {
return IndexFromRemote(input.Identifier)
}
return IndexFromRemote(input.Identifier)
}
func (i *ImageSpec) ForPlatforms(platform string) ([]*ImageSpec, error) {

View File

@@ -11,20 +11,20 @@ type policyEvaluatorCtxKeyType struct{}
var PolicyEvaluatorCtxKey policyEvaluatorCtxKeyType
// sets PolicyEvaluator in context
func WithPolicyEvaluator(ctx context.Context, pe PolicyEvaluator) context.Context {
// sets PolicyEvaluator in context.
func WithPolicyEvaluator(ctx context.Context, pe Evaluator) context.Context {
return context.WithValue(ctx, PolicyEvaluatorCtxKey, pe)
}
// gets PolicyEvaluator from context, defaults to Rego PolicyEvaluator if not set
func GetPolicyEvaluator(ctx context.Context) (PolicyEvaluator, error) {
t, ok := ctx.Value(PolicyEvaluatorCtxKey).(PolicyEvaluator)
// gets PolicyEvaluator from context, defaults to Rego PolicyEvaluator if not set.
func GetPolicyEvaluator(ctx context.Context) (Evaluator, error) {
t, ok := ctx.Value(PolicyEvaluatorCtxKey).(Evaluator)
if !ok {
return nil, fmt.Errorf("no policy evaluator client set on context (set one with policy.WithPolicyEvaluator)")
}
return t, nil
}
type PolicyEvaluator interface {
Evaluate(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error)
type Evaluator interface {
Evaluate(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *Input) (*Result, error)
}

View File

@@ -7,19 +7,19 @@ import (
)
type MockPolicyEvaluator struct {
EvaluateFunc func(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error)
EvaluateFunc func(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *Input) (*Result, error)
}
func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error) {
func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *Input) (*Result, error) {
if pe.EvaluateFunc != nil {
return pe.EvaluateFunc(ctx, resolver, pctx, input)
}
return AllowedResult(), nil
}
func GetMockPolicy() PolicyEvaluator {
func GetMockPolicy() Evaluator {
return &MockPolicyEvaluator{
EvaluateFunc: func(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error) {
EvaluateFunc: func(_ context.Context, _ oci.AttestationResolver, _ *Policy, _ *Input) (*Result, error) {
return AllowedResult(), nil
},
}

View File

@@ -12,11 +12,11 @@ import (
"github.com/docker/attest/pkg/oci"
)
func resolveLocalPolicy(opts *PolicyOptions, mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
func resolveLocalPolicy(opts *Options, mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
if opts.LocalPolicyDir == "" {
return nil, fmt.Errorf("local policy dir not set")
}
files := make([]*PolicyFile, 0, len(mapping.Files))
files := make([]*File, 0, len(mapping.Files))
for _, f := range mapping.Files {
filename := f.Path
filePath := path.Join(opts.LocalPolicyDir, filename)
@@ -24,7 +24,7 @@ func resolveLocalPolicy(opts *PolicyOptions, mapping *config.PolicyMapping, imag
if err != nil {
return nil, fmt.Errorf("failed to read policy file %s: %w", filename, err)
}
files = append(files, &PolicyFile{
files = append(files, &File{
Path: filename,
Content: fileContents,
})
@@ -39,15 +39,15 @@ func resolveLocalPolicy(opts *PolicyOptions, mapping *config.PolicyMapping, imag
return policy, nil
}
func resolveTufPolicy(opts *PolicyOptions, mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
files := make([]*PolicyFile, 0, len(mapping.Files))
func resolveTUFPolicy(opts *Options, mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
files := make([]*File, 0, len(mapping.Files))
for _, f := range mapping.Files {
filename := f.Path
_, fileContents, err := opts.TufClient.DownloadTarget(filename, filepath.Join(opts.LocalTargetsDir, filename))
_, fileContents, err := opts.TUFClient.DownloadTarget(filename, filepath.Join(opts.LocalTargetsDir, filename))
if err != nil {
return nil, fmt.Errorf("failed to download policy file %s: %w", filename, err)
}
files = append(files, &PolicyFile{
files = append(files, &File{
Path: filename,
Content: fileContents,
})
@@ -88,12 +88,12 @@ func findPolicyMatchImpl(imageName string, mappings *config.PolicyMappings, matc
for _, rule := range mappings.Rules {
if rule.Pattern.MatchString(imageName) {
switch {
case rule.PolicyId == "" && rule.Replacement == "":
case rule.PolicyID == "" && rule.Replacement == "":
return nil, fmt.Errorf("rule %s has neither policy-id nor rewrite", rule.Pattern)
case rule.PolicyId != "" && rule.Replacement != "":
case rule.PolicyID != "" && rule.Replacement != "":
return nil, fmt.Errorf("rule %s has both policy-id and rewrite", rule.Pattern)
case rule.PolicyId != "":
policy := mappings.Policies[rule.PolicyId]
case rule.PolicyID != "":
policy := mappings.Policies[rule.PolicyID]
if policy != nil {
return &policyMatch{
matchType: matchTypePolicy,
@@ -120,35 +120,35 @@ func findPolicyMatchImpl(imageName string, mappings *config.PolicyMappings, matc
return &policyMatch{matchType: matchTypeNoMatch, matchedName: imageName}, nil
}
func resolvePolicyById(opts *PolicyOptions) (*Policy, error) {
if opts.PolicyId != "" {
func resolvePolicyByID(opts *Options) (*Policy, error) {
if opts.PolicyID != "" {
localMappings, err := config.LoadLocalMappings(opts.LocalPolicyDir)
if err != nil {
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
}
if localMappings != nil {
policy := localMappings.Policies[opts.PolicyId]
policy := localMappings.Policies[opts.PolicyID]
if policy != nil {
return resolveLocalPolicy(opts, policy, "", "")
}
}
// must check tuf
tufMappings, err := config.LoadTufMappings(opts.TufClient, opts.LocalTargetsDir)
tufMappings, err := config.LoadTUFMappings(opts.TUFClient, opts.LocalTargetsDir)
if err != nil {
return nil, fmt.Errorf("failed to load tuf policy mappings by id: %w", err)
}
policy := tufMappings.Policies[opts.PolicyId]
policy := tufMappings.Policies[opts.PolicyID]
if policy != nil {
return resolveTufPolicy(opts, policy, "", "")
return resolveTUFPolicy(opts, policy, "", "")
}
return nil, fmt.Errorf("policy with id %s not found", opts.PolicyId)
return nil, fmt.Errorf("policy with id %s not found", opts.PolicyID)
}
return nil, nil
}
func ResolvePolicy(ctx context.Context, detailsResolver oci.ImageDetailsResolver, opts *PolicyOptions) (*Policy, error) {
p, err := resolvePolicyById(opts)
func ResolvePolicy(ctx context.Context, detailsResolver oci.ImageDetailsResolver, opts *Options) (*Policy, error) {
p, err := resolvePolicyByID(opts)
if err != nil {
return nil, fmt.Errorf("failed to resolve policy by id: %w", err)
}
@@ -175,7 +175,7 @@ func ResolvePolicy(ctx context.Context, detailsResolver oci.ImageDetailsResolver
return resolveLocalPolicy(opts, match.policy, imageName, match.matchedName)
}
// must check tuf
tufMappings, err := config.LoadTufMappings(opts.TufClient, opts.LocalTargetsDir)
tufMappings, err := config.LoadTUFMappings(opts.TUFClient, opts.LocalTargetsDir)
if err != nil {
return nil, fmt.Errorf("failed to load tuf policy mappings as fallback: %w", err)
}
@@ -183,8 +183,8 @@ func ResolvePolicy(ctx context.Context, detailsResolver oci.ImageDetailsResolver
// it's a mirror of a tuf policy
if match.matchType == matchTypeMatchNoPolicy {
for _, mapping := range tufMappings.Policies {
if mapping.Id == match.rule.PolicyId {
return resolveTufPolicy(opts, mapping, imageName, match.matchedName)
if mapping.ID == match.rule.PolicyID {
return resolveTUFPolicy(opts, mapping, imageName, match.matchedName)
}
}
}
@@ -195,7 +195,7 @@ func ResolvePolicy(ctx context.Context, detailsResolver oci.ImageDetailsResolver
return nil, err
}
if match.matchType == matchTypePolicy {
return resolveTufPolicy(opts, match.policy, imageName, match.matchedName)
return resolveTUFPolicy(opts, match.policy, imageName, match.matchedName)
}
return nil, nil
}
@@ -229,7 +229,7 @@ func CreateAttestationResolver(resolver oci.ImageDetailsResolver, mapping *confi
}
return oci.NewReferrersAttestationResolver(resolver)
}
case *oci.OCILayoutResolver:
case *oci.LayoutResolver:
if mapping.Attestations != nil && mapping.Attestations.Style == config.AttestationStyleAttached {
return resolver, nil
} else {

View File

@@ -112,7 +112,7 @@ func TestFindPolicyMatch(t *testing.T) {
assert.Equal(t, tc.expectedMatchType, match.matchType)
if match.matchType == matchTypePolicy {
if assert.NotNil(t, match.policy) {
assert.Equal(t, tc.expectedPolicyID, match.policy.Id)
assert.Equal(t, tc.expectedPolicyID, match.policy.ID)
}
}
assert.Equal(t, tc.expectedImageName, match.matchedName)

View File

@@ -47,13 +47,13 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
expectSuccess bool
isCanonical bool
resolver oci.AttestationResolver
policy *policy.PolicyOptions
policyId string
policy *policy.Options
policyID string
errorStr string
}{
{repo: "testdata/mock-tuf-allow", expectSuccess: true, isCanonical: false, resolver: defaultResolver},
{repo: "testdata/mock-tuf-allow", expectSuccess: true, isCanonical: false, resolver: defaultResolver, policyId: "docker-official-images"},
{repo: "testdata/mock-tuf-allow", expectSuccess: false, isCanonical: false, resolver: defaultResolver, policyId: "non-existent-policy-id", errorStr: errorStr},
{repo: "testdata/mock-tuf-allow", expectSuccess: true, isCanonical: false, resolver: defaultResolver, policyID: "docker-official-images"},
{repo: "testdata/mock-tuf-allow", expectSuccess: false, isCanonical: false, resolver: defaultResolver, policyID: "non-existent-policy-id", errorStr: errorStr},
{repo: "testdata/mock-tuf-deny", expectSuccess: false, isCanonical: false, resolver: defaultResolver},
{repo: "testdata/mock-tuf-verify-sig", expectSuccess: true, isCanonical: false, resolver: defaultResolver},
{repo: "testdata/mock-tuf-wrong-key", expectSuccess: false, isCanonical: false, resolver: defaultResolver},
@@ -63,18 +63,18 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.repo, func(t *testing.T) {
input := &policy.PolicyInput{
input := &policy.Input{
Digest: "sha256:test-digest",
Purl: "test-purl",
PURL: "test-purl",
IsCanonical: tc.isCanonical,
}
tufClient := tuf.NewMockTufClient(tc.repo, test.CreateTempDir(t, "", "tuf-dest"))
if tc.policy == nil {
tc.policy = &policy.PolicyOptions{
TufClient: tufClient,
tc.policy = &policy.Options{
TUFClient: tufClient,
LocalTargetsDir: test.CreateTempDir(t, "", "tuf-targets"),
PolicyId: tc.policyId,
PolicyID: tc.policyID,
}
}
imageName, err := tc.resolver.ImageName(ctx)
@@ -110,8 +110,8 @@ func TestLoadingMappings(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, len(policyMappings.Rules), 3)
for _, mirror := range policyMappings.Rules {
if mirror.PolicyId != "" {
assert.Equal(t, "docker-official-images", mirror.PolicyId)
if mirror.PolicyID != "" {
assert.Equal(t, "docker-official-images", mirror.PolicyID)
}
}
}

View File

@@ -30,13 +30,13 @@ const (
resultBinding = "result"
)
func NewRegoEvaluator(debug bool) PolicyEvaluator {
func NewRegoEvaluator(debug bool) Evaluator {
return &regoEvaluator{
debug: debug,
}
}
func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *PolicyInput) (*Result, error) {
func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, pctx *Policy, input *Input) (*Result, error) {
var regoOpts []func(*rego.Rego)
// Create a new in-memory store
@@ -113,7 +113,7 @@ func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationR
}
func jsonGenerator[T any]() func(t *ast.Term, ec *rego.EvalContext) (any, error) {
return func(t *ast.Term, ec *rego.EvalContext) (any, error) {
return func(t *ast.Term, _ *rego.EvalContext) (any, error) {
// TODO: this is horrible - we're converting the AST to JSON and then back to AST, then using ast.As to convert it to a struct
// We can't use ast.As directly because it fails if the AST contains a set
json, err := ast.JSON(t.Value)
@@ -164,9 +164,9 @@ func handleErrors1(f func(rCtx rego.BuiltinContext, a *ast.Term) (*ast.Term, err
}
}
func handleErrors2(f func(rCtx rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error)) rego.Builtin2 {
func handleErrors2(f func(rCtx *rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error)) rego.Builtin2 {
return func(rCtx rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) {
return wrapFunctionResult(f(rCtx, a, b))
return wrapFunctionResult(f(&rCtx, a, b))
}
}
@@ -181,7 +181,7 @@ func RegoFunctions(resolver oci.AttestationResolver) []*tester.Builtin {
Memoize: true,
Nondeterministic: verifyDecl.Nondeterministic,
},
handleErrors2(verifyIntotoEnvelope)),
handleErrors2(verifyInTotoEnvelope)),
},
{
Decl: attestDecl,
@@ -192,12 +192,12 @@ func RegoFunctions(resolver oci.AttestationResolver) []*tester.Builtin {
Memoize: true,
Nondeterministic: attestDecl.Nondeterministic,
},
handleErrors1(fetchIntotoAttestations(resolver))),
handleErrors1(fetchInTotoAttestations(resolver))),
},
}
}
func fetchIntotoAttestations(resolver oci.AttestationResolver) rego.Builtin1 {
func fetchInTotoAttestations(resolver oci.AttestationResolver) rego.Builtin1 {
return func(rCtx rego.BuiltinContext, predicateTypeTerm *ast.Term) (*ast.Term, error) {
predicateTypeStr, ok := predicateTypeTerm.Value.(ast.String)
if !ok {
@@ -227,7 +227,7 @@ func fetchIntotoAttestations(resolver oci.AttestationResolver) rego.Builtin1 {
}
}
func verifyIntotoEnvelope(rCtx rego.BuiltinContext, envTerm, optsTerm *ast.Term) (*ast.Term, error) {
func verifyInTotoEnvelope(rCtx *rego.BuiltinContext, envTerm, optsTerm *ast.Term) (*ast.Term, error) {
env := new(att.Envelope)
opts := new(att.VerifyOptions)
err := ast.As(envTerm.Value, env)

View File

@@ -26,29 +26,29 @@ type Result struct {
Summary Summary `json:"summary"`
}
type PolicyOptions struct {
TufClient tuf.TUFClient
type Options struct {
TUFClient tuf.Downloader
LocalTargetsDir string
LocalPolicyDir string
PolicyId string
PolicyID string
ReferrersRepo string
AttestationStyle config.AttestationStyle
}
type Policy struct {
InputFiles []*PolicyFile
InputFiles []*File
Query string
Mapping *config.PolicyMapping
ResolvedName string
}
type PolicyInput struct {
type Input struct {
Digest string `json:"digest"`
Purl string `json:"purl"`
PURL string `json:"purl"`
IsCanonical bool `json:"isCanonical"`
}
type PolicyFile struct {
type File struct {
Path string
Content []byte
}

View File

@@ -9,18 +9,18 @@ import (
awssigner "github.com/sigstore/sigstore/pkg/signature/kms/aws"
)
// using AWS KMS
func GetAWSSigner(ctx context.Context, keyArn string, region string) (dsse.SignerVerifier, error) {
keypath := fmt.Sprintf("awskms:///%s", keyArn)
sv, err := awssigner.LoadSignerVerifier(ctx, keypath, config.WithRegion(region))
// using AWS KMS.
func GetAWSSigner(ctx context.Context, keyARN string, region string) (dsse.SignerVerifier, error) {
keyPath := fmt.Sprintf("awskms:///%s", keyARN)
sv, err := awssigner.LoadSignerVerifier(ctx, keyPath, config.WithRegion(region))
if err != nil {
return nil, fmt.Errorf("error loading aws signer verifier: %w", err)
}
cs, _, err := sv.CryptoSigner(context.Background(), func(err error) {})
cs, _, err := sv.CryptoSigner(context.Background(), func(_ error) {})
if err != nil {
return nil, fmt.Errorf("error getting aws crypto signer: %w", err)
}
signer := &ECDSA256_SignerVerifier{
signer := &ECDSA256SignerVerifier{
Signer: cs,
}
return signer, nil

View File

@@ -14,12 +14,12 @@ import (
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
type ECDSA256_SignerVerifier struct {
type ECDSA256SignerVerifier struct {
crypto.Signer
}
// implement keyid function
func (s *ECDSA256_SignerVerifier) KeyID() (string, error) {
// implement keyid function.
func (s *ECDSA256SignerVerifier) KeyID() (string, error) {
keyid, err := KeyID(s.Signer.Public())
if err != nil {
return "", fmt.Errorf("error getting keyid: %w", err)
@@ -27,15 +27,15 @@ func (s *ECDSA256_SignerVerifier) KeyID() (string, error) {
return keyid, nil
}
func (s *ECDSA256_SignerVerifier) Public() crypto.PublicKey {
func (s *ECDSA256SignerVerifier) Public() crypto.PublicKey {
return s.Signer.Public()
}
func (s *ECDSA256_SignerVerifier) Sign(ctx context.Context, data []byte) ([]byte, error) {
func (s *ECDSA256SignerVerifier) Sign(_ context.Context, data []byte) ([]byte, error) {
return s.Signer.Sign(rand.Reader, data, crypto.SHA256)
}
func (s *ECDSA256_SignerVerifier) Verify(ctx context.Context, data []byte, sig []byte) error {
func (s *ECDSA256SignerVerifier) Verify(_ context.Context, data []byte, sig []byte) error {
pub, ok := s.Signer.Public().(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("public key is not ecdsa")
@@ -52,7 +52,7 @@ func LoadKeyPair(priv []byte) (dsse.SignerVerifier, error) {
if err != nil {
return nil, err
}
return &ECDSA256_SignerVerifier{
return &ECDSA256SignerVerifier{
Signer: privateKey,
}, nil
}
@@ -78,7 +78,7 @@ func GenKeyPair() (dsse.SignerVerifier, error) {
if err != nil {
return nil, err
}
return &ECDSA256_SignerVerifier{
return &ECDSA256SignerVerifier{
Signer: signer,
}, nil
}

View File

@@ -10,18 +10,18 @@ import (
)
// using GCP KMS
// reference should be in the format projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION]
// reference should be in the format projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION].
func GetGCPSigner(ctx context.Context, reference string, opts ...option.ClientOption) (dsse.SignerVerifier, error) {
reference = fmt.Sprintf("gcpkms://%s", reference)
sv, err := gcpsigner.LoadSignerVerifier(ctx, reference, opts...)
if err != nil {
return nil, fmt.Errorf("error loading gcp signer verifier: %w", err)
}
cs, _, err := sv.CryptoSigner(ctx, func(err error) {})
cs, _, err := sv.CryptoSigner(ctx, func(_ error) {})
if err != nil {
return nil, fmt.Errorf("error getting gcp crypto signer: %w", err)
}
signer := &ECDSA256_SignerVerifier{
signer := &ECDSA256SignerVerifier{
Signer: cs,
}
return signer, nil

View File

@@ -37,7 +37,7 @@ func TestGCPKMS_Signer(t *testing.T) {
keyId, err := signer.KeyID()
require.NoError(t, err)
assert.NotEmpty(t, keyId)
publicKey, err := Parse([]byte(publicKeyPEM))
publicKey, err := ParsePublicKey([]byte(publicKeyPEM))
require.NoError(t, err)
// verify payload ecdsa signature
ok := ecdsa.VerifyASN1(publicKey, hash, sig)

View File

@@ -11,7 +11,7 @@ import (
func KeyID(pubKey crypto.PublicKey) (string, error) {
pub, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return "", fmt.Errorf("error marshalling public key: %w", err)
return "", fmt.Errorf("error marshaling public key: %w", err)
}
return util.SHA256Hex(pub), nil
}

View File

@@ -9,7 +9,7 @@ import (
const pemType = "PUBLIC KEY"
func Parse(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
func ParsePublicKey(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
p, _ := pem.Decode(pubkeyBytes)
if p == nil {
return nil, fmt.Errorf("pubkey file does not contain any PEM data")
@@ -29,7 +29,7 @@ func Parse(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
return ecdsaPubKey, nil
}
func ToPEM(ecdsaPubKey *ecdsa.PublicKey) ([]byte, error) {
func ConvertToPEM(ecdsaPubKey *ecdsa.PublicKey) ([]byte, error) {
pubKeyBytes, err := x509.MarshalPKIXPublicKey(ecdsaPubKey)
if err != nil {
return nil, fmt.Errorf("error failed to marshal public key: %w", err)

View File

@@ -10,7 +10,7 @@ import (
)
const (
USE_MOCK_TL = true
UseMockTL = true
TestEntry = `{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5Zjg2ZDA4MTg4NGM3ZDY1OWEyZmVhYTBjNTVhZDAxNWEzYmY0ZjFiMmIwYjgyMmNkMTVkNmMxNWIwZjAwYTA4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJQUlyVUZGUzBIYmNzZjc5L08yajVXdHl2R2Vvd1NVSXpZcDlBM2IwWnREVUFpQVQxZU42ZjFyVmVWa011REFlN3dxWkJ2bE5LY2VsajNVVDNmaWhyQjZSY2c9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVSlZla05DSzJGQlJFRm5SVU5CWjBWQ1RVRnZSME5EY1VkVFRUUTVRa0ZOUTAxQk9IaEVWRUZNUW1kT1ZrSkJUVlJDU0ZKc1l6TlJkMGhvWTA0S1RXcE5lRTFxU1ROTlZHdDVUWHBWTlZkb1kwNU5hbEY0VFdwSk1rMVVhM2xOZWxVMVYycEJVRTFSTUhkRGQxbEVWbEZSUkVWM1VqQmFXRTR3VFVacmR3cEZkMWxJUzI5YVNYcHFNRU5CVVZsSlMyOWFTWHBxTUVSQlVXTkVVV2RCUlVRMFZpdFNSV2g0SzJGeFYwZzNlV3hOVFVSSVlXaE9UVzVOVEZOUFNsQXZDamxyUVcwNWJIQXJNMjF4V1ZSQmFGVlNjbUUyVDBRMVVYZzRXbUprSzJWMVVIbFFhemw1SzNjdloxZEhSRUk1ZW00dlNXd3hTMDVIVFVWUmQwUm5XVVFLVmxJd1VFRlJTQzlDUVZGRVFXZGxRVTFDVFVkQk1WVmtTbEZSVFUxQmIwZERRM05IUVZGVlJrSjNUVVJOUVhkSFFURlZaRVYzUlVJdmQxRkRUVUZCZHdwRWQxbEVWbEl3VWtKQlozZENiMGxGWkVkV2VtUkVRVXRDWjJkeGFHdHFUMUJSVVVSQlowNUtRVVJDUjBGcFJVRTNOMjFFTDFSbVJtRlJVemxrWlhRMENqbFhaRk41YURKT1VTOUZiMVJtYVVGdFFtaHVWblpEVTNSUVowTkpVVU1yZDNSdllpOU9iMUp4T0c5cU4wZDNibTVKYUZKVGRDOVJNbmtyVXpoUkwzSUthRkpVYW5GaE9HZExRVDA5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0=","integratedTime":1703705039,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d","logIndex":59674396,"verification":{"inclusionProof":{"checkpoint":"rekor.sigstore.dev - 2605736670972794746\n55510966\nJCi1O53Xmdi9lXnui4Q5SQ+MJSMnWr1Bxn+Q2Qf22tU=\nTimestamp: 1703705040158839214\n\n— rekor.sigstore.dev wNI9ajBFAiAXgtjFDVqCSgiSP04TQzELrz4+EyBwyYVL2EEULTCy0AIhAI9peLU76ZUD1tvU8qvzBJBo77IYD1rc+A1MPc35AeVK\n","hashes":["fb77ee213b48f4b18dc81c6e634c570abf99b257713561f174f2e0f4c039af67","6cb113bbefadecbbb8b89b1c08232438a6125071790b6a062cff8c1ccfdcb91e","6fbe1424e264e4590ca502d671b7a036c87f7a90d1f57534b98eb781144160bf","077b606720a6478200f6c3ed08a68e9b01b1cae192cb120888ddcc95521601bd","b6f8e8bc21ae0cde82b92422a4b4f37b28a43185821e468a4e65b6c79ed8f5b7","89332533fac54e9bc68c7353c42f6ebb9fe38039f67910332ff95082072068d4","0814d6f707a75fb3334bab14ab5466bd8b9a64ae7be7cd4d53a428c64932bc66","e883e826f10329c63a4a2ed21156037a050df43b9d74079296beac6968ed4150","d79230703257b7e4a8a61b032b6980d1a0bdbc7ae96ca838b525b3751785fe48","2f4a77e5288462cd3b75084d37f1502dcbe0943d18dd95cb247fc1ebbabc0aad","38562c253d3536d0d00e3547c880b6b0251a25ac69605b50c9eaa1a27186cc7a","9dea192350ff8b3c0f5ccda38261cb38ebd61869281c3928912332d1144e0a04","2c4d25ba59aa573ab2c79c2d3cd9e1d74789b10632432724d63112ce50b44874","98c486feb5d87092a78a46c4b5be04868654900affc2e86ffb20074dc73a883a","6969c49bd73f19bf28a5eaeabd331ddd60502defb2cd3d96e17b741c80adec6c"],"logIndex":55510965,"rootHash":"2428b53b9dd799d8bd9579ee8b8439490f8c2523275abd41c67f90d907f6dad5","treeSize":55510966},"signedEntryTimestamp":"MEUCIQCG9PRI8PcvtJyE9pbcculZipze6NEWR1Nk8EYocto3BwIgYu5gqgjW80HMjSjUxUNJLp0wlVTesnJCeByUBySc59w="}}`
)
@@ -26,10 +26,10 @@ func GetMockTL() TL {
}
return &MockTL{
UploadLogEntryFunc: func(ctx context.Context, subject string, payload []byte, signature []byte, signer dsse.SignerVerifier) ([]byte, error) {
UploadLogEntryFunc: func(_ context.Context, _ string, _ []byte, _ []byte, _ dsse.SignerVerifier) ([]byte, error) {
return []byte(TestEntry), nil
},
VerifyLogEntryFunc: func(ctx context.Context, entryBytes []byte) (time.Time, error) {
VerifyLogEntryFunc: func(_ context.Context, entryBytes []byte) (time.Time, error) {
// return the integrated time in the log entry without any checking
le, err := unmarshalEntry(entryBytes)
if err != nil {
@@ -40,7 +40,7 @@ func GetMockTL() TL {
}
return time.Unix(*le.IntegratedTime, 0), nil
},
VerifyEntryPayloadFunc: func(entryBytes, payload, pkToken []byte) error {
VerifyEntryPayloadFunc: func(_, _, _ []byte) error {
return nil
},
UnmarshalEntryFunc: func(entry []byte) (any, error) {

View File

@@ -1,6 +1,7 @@
package tlog
import (
"bytes"
"context"
"crypto/rand"
"crypto/sha256"
@@ -31,23 +32,23 @@ const (
type tlCtxKeyType struct{}
var TlCtxKey tlCtxKeyType
var TLCtxKey tlCtxKeyType
// sets TL in context
// sets TL in context.
func WithTL(ctx context.Context, tl TL) context.Context {
return context.WithValue(ctx, TlCtxKey, tl)
return context.WithValue(ctx, TLCtxKey, tl)
}
// gets TL from context, defaults to Rekor TL if not set
// gets TL from context, defaults to Rekor TL if not set.
func GetTL(ctx context.Context) TL {
t, ok := ctx.Value(TlCtxKey).(TL)
t, ok := ctx.Value(TLCtxKey).(TL)
if !ok {
t = &RekorTL{}
}
return t
}
type TlPayload struct {
type TLPayload struct {
Algorithm string
Hash string
Signature string
@@ -98,7 +99,7 @@ func (tl *MockTL) UnmarshalEntry(entryBytes []byte) (any, error) {
type RekorTL struct{}
// UploadLogEntry submits a PK token signature to the transparency log
// UploadLogEntry submits a PK token signature to the transparency log.
func (tl *RekorTL) UploadLogEntry(ctx context.Context, subject string, payload, signature []byte, signer dsse.SignerVerifier) ([]byte, error) {
// generate self-signed x509 cert
pubCert, err := CreateX509Cert(subject, signer)
@@ -121,12 +122,12 @@ func (tl *RekorTL) UploadLogEntry(ctx context.Context, subject string, payload,
}
entryBytes, err := entry.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("error marshalling TL entry: %w", err)
return nil, fmt.Errorf("error marshaling TL entry: %w", err)
}
return entryBytes, nil
}
// VerifyLogEntry verifies a transparency log entry
// VerifyLogEntry verifies a transparency log entry.
func (tl *RekorTL) VerifyLogEntry(ctx context.Context, entryBytes []byte) (time.Time, error) {
zeroTime := time.Time{}
entry, err := tl.UnmarshalEntry(entryBytes)
@@ -157,12 +158,12 @@ func (tl *RekorTL) VerifyLogEntry(ctx context.Context, entryBytes []byte) (time.
return integratedTime, nil
}
// CreateX509Cert generates a self-signed x509 cert for TL submission
// CreateX509Cert generates a self-signed x509 cert for TL submission.
func CreateX509Cert(subject string, signer dsse.SignerVerifier) ([]byte, error) {
// encode ephemeral public key
ecPub, err := x509.MarshalPKIXPublicKey(signer.Public())
if err != nil {
return nil, fmt.Errorf("error marshalling public key: %w", err)
return nil, fmt.Errorf("error marshaling public key: %w", err)
}
template := x509.Certificate{
@@ -180,7 +181,7 @@ func CreateX509Cert(subject string, signer dsse.SignerVerifier) ([]byte, error)
// dsse.SignerVerifier doesn't implement cypto.Signer exactly
csigner, ok := signer.(*signerverifier.ECDSA256_SignerVerifier)
csigner, ok := signer.(*signerverifier.ECDSA256SignerVerifier)
if !ok {
return nil, fmt.Errorf("expected signer to be of type *signerverifier.ECDSA_SignerVerifier, got %T", signer)
}
@@ -193,7 +194,7 @@ func CreateX509Cert(subject string, signer dsse.SignerVerifier) ([]byte, error)
return pem.EncodeToMemory(certBlock), nil
}
// VerifyEntryPayload checks that the TL entry payload matches envelope payload
// VerifyEntryPayload checks that the TL entry payload matches envelope payload.
func (tl *RekorTL) VerifyEntryPayload(entryBytes, payload, publicKey []byte) error {
entry, err := tl.UnmarshalEntry(entryBytes)
if err != nil {
@@ -228,7 +229,7 @@ func (tl *RekorTL) VerifyEntryPayload(entryBytes, payload, publicKey []byte) err
if err != nil {
return fmt.Errorf("failed to parse certificate: %w", err)
}
if string(result.RawSubjectPublicKeyInfo) != string(publicKey) {
if !bytes.Equal(result.RawSubjectPublicKeyInfo, publicKey) {
return fmt.Errorf("error payload and tl entry public key mismatch")
}
return nil
@@ -243,9 +244,9 @@ func (tl *RekorTL) UnmarshalEntry(entry []byte) (any, error) {
return le, nil
}
func extractHashedRekord(Body string) (*TlPayload, error) {
sig := new(TlPayload)
pe, err := models.UnmarshalProposedEntry(base64.NewDecoder(base64.StdEncoding, strings.NewReader(Body)), runtime.JSONConsumer())
func extractHashedRekord(body string) (*TLPayload, error) {
sig := new(TLPayload)
pe, err := models.UnmarshalProposedEntry(base64.NewDecoder(base64.StdEncoding, strings.NewReader(body)), runtime.JSONConsumer())
if err != nil {
return nil, err
}

View File

@@ -14,7 +14,7 @@ import (
)
const (
// test artifacts
// test artifacts.
TestPayload = "test"
TestPublicKey = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAED4V+REhx+aqWH7ylMMDHahNMnMLS\nOJP/9kAm9lp+3mqYTAhURra6OD5Qx8Zbd+euPyPk9y+w/gWGDB9zn/Il1A==\n-----END PUBLIC KEY-----"
)
@@ -53,15 +53,15 @@ func TestUploadAndVerifyLogEntry(t *testing.T) {
assert.NoError(t, err)
var tl TL
if USE_MOCK_TL {
if UseMockTL {
tl = &MockTL{
UploadLogEntryFunc: func(ctx context.Context, subject string, payload []byte, signature []byte, signer dsse.SignerVerifier) ([]byte, error) {
UploadLogEntryFunc: func(_ context.Context, _ string, _ []byte, _ []byte, _ dsse.SignerVerifier) ([]byte, error) {
return []byte(TestEntry), nil
},
VerifyLogEntryFunc: func(ctx context.Context, entryBytes []byte) (time.Time, error) {
VerifyLogEntryFunc: func(_ context.Context, _ []byte) (time.Time, error) {
return time.Time{}, nil
},
VerifyEntryPayloadFunc: func(entryBytes, payload, publicKey []byte) error {
VerifyEntryPayloadFunc: func(_, _, _ []byte) error {
return nil
},
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/theupdateframework/go-tuf/v2/metadata"
)
func ExampleNewTufClient_registry() {
func ExampleNewClient_registry() {
// create a tuf client
home, err := os.UserHomeDir()
if err != nil {
@@ -21,7 +21,7 @@ func ExampleNewTufClient_registry() {
metadataURI := "registry-1.docker.io/docker/tuf-metadata:latest"
targetsURI := "registry-1.docker.io/docker/tuf-targets"
registryClient, err := tuf.NewTufClient(embed.RootStaging.Data, tufOutputPath, metadataURI, targetsURI, tuf.NewMockVersionChecker())
registryClient, err := tuf.NewClient(embed.RootStaging.Data, tufOutputPath, metadataURI, targetsURI, tuf.NewMockVersionChecker())
if err != nil {
panic(err)
}

View File

@@ -6,25 +6,25 @@ import (
"path/filepath"
)
type mockTufClient struct {
type MockTufClient struct {
srcPath string
dstPath string
}
func NewMockTufClient(srcPath string, dstPath string) *mockTufClient {
func NewMockTufClient(srcPath string, dstPath string) *MockTufClient {
if srcPath == "" {
panic("srcPath must be set")
}
if dstPath == "" {
panic("dstPath must be set")
}
return &mockTufClient{
return &MockTufClient{
srcPath: srcPath,
dstPath: dstPath,
}
}
func (dc *mockTufClient) DownloadTarget(target string, filePath string) (actualFilePath string, data []byte, err error) {
func (dc *MockTufClient) DownloadTarget(target string, filePath string) (actualFilePath string, data []byte, err error) {
src, err := os.Open(filepath.Join(dc.srcPath, target))
if err != nil {
return "", nil, err
@@ -59,14 +59,14 @@ func (dc *mockTufClient) DownloadTarget(target string, filePath string) (actualF
return dstFilePath, b, nil
}
type mockVersionChecker struct {
type MockVersionChecker struct {
err error
}
func NewMockVersionChecker() *mockVersionChecker {
return &mockVersionChecker{}
func NewMockVersionChecker() *MockVersionChecker {
return &MockVersionChecker{}
}
func (vc *mockVersionChecker) CheckVersion(client TUFClient) error {
func (vc *MockVersionChecker) CheckVersion(_ Downloader) error {
return vc.err
}

View File

@@ -19,14 +19,14 @@ import (
)
const (
TufFileNameAnnotation = "tuf.io/filename"
TUFFileNameAnnotation = "tuf.io/filename"
)
type TufRole string
type Role string
var TufRoles = []TufRole{metadata.ROOT, metadata.SNAPSHOT, metadata.TARGETS, metadata.TIMESTAMP}
var Roles = []Role{metadata.ROOT, metadata.SNAPSHOT, metadata.TARGETS, metadata.TIMESTAMP}
// RegistryFetcher implements Fetcher
// RegistryFetcher implements Fetcher.
type RegistryFetcher struct {
httpUserAgent string
metadataRepo string
@@ -46,13 +46,13 @@ func NewImageCache() *ImageCache {
}
}
// Get image from cache
// Get image from cache.
func (c *ImageCache) Get(imgRef string) ([]byte, bool) {
img, found := c.cache[imgRef]
return img, found
}
// Add image to cache
// Add image to cache.
func (c *ImageCache) Put(imgRef string, img []byte) {
c.cache[imgRef] = img
}
@@ -113,7 +113,7 @@ func (d *RegistryFetcher) DownloadFile(urlPath string, maxLength int64, timeout
}
}
// getManifest returns the manifest for an image or index
// getManifest returns the manifest for an image or index.
func (d *RegistryFetcher) getManifest(ref string) ([]byte, error) {
// Pull image manifest
var err error
@@ -135,7 +135,7 @@ func (d *RegistryFetcher) getManifest(ref string) ([]byte, error) {
return mf, nil
}
// pullFileLayer pulls a layer for an image or index and returns its data
// pullFileLayer pulls a layer for an image or index and returns its data.
func (d *RegistryFetcher) pullFileLayer(ref string, maxLength int64) ([]byte, error) {
var data []byte
var found bool
@@ -159,7 +159,7 @@ func (d *RegistryFetcher) pullFileLayer(ref string, maxLength int64) ([]byte, er
return data, nil
}
// getDataFromLayer returns the data from a layer in an image
// getDataFromLayer returns the data from a layer in an image.
func getDataFromLayer(fileLayer v1.Layer, maxLength int64) ([]byte, error) {
length, err := fileLayer.Size()
if err != nil {
@@ -185,7 +185,7 @@ func getDataFromLayer(fileLayer v1.Layer, maxLength int64) ([]byte, error) {
return data, nil
}
// parseImgRef maintains the Fetcher interface by parsing a URL path to an image reference and file name
// parseImgRef maintains the Fetcher interface by parsing a URL path to an image reference and file name.
func (d *RegistryFetcher) parseImgRef(urlPath string) (imgRef, fileName string, err error) {
// Check if repo is target or metadata
if strings.Contains(urlPath, d.targetsRepo) {
@@ -208,12 +208,11 @@ func (d *RegistryFetcher) parseImgRef(urlPath string) (imgRef, fileName string,
return fmt.Sprintf("%s:%s", d.metadataRepo, role), fileName, nil
}
return fmt.Sprintf("%s:%s", d.metadataRepo, d.metadataTag), fileName, nil
} else {
return "", "", fmt.Errorf("urlPath: %s must be in metadata or targets repo", urlPath)
}
return "", "", fmt.Errorf("urlPath: %s must be in metadata or targets repo", urlPath)
}
// findFileInManifest searches the image or index manifest for a file with the given name and returns its digest
// findFileInManifest searches the image or index manifest for a file with the given name and returns its digest.
func (d *RegistryFetcher) findFileInManifest(mf []byte, name string) (*v1.Hash, error) {
var index bool
@@ -226,20 +225,21 @@ func (d *RegistryFetcher) findFileInManifest(mf []byte, name string) (*v1.Hash,
// determine image or index manifest
var layers []Layer
if l.MediaType == string(types.OCIImageIndex) {
switch l.MediaType {
case string(types.OCIImageIndex):
layers = l.Manifests
index = true
} else if l.MediaType == string(types.OCIManifestSchema1) {
case string(types.OCIManifestSchema1):
layers = l.Layers
index = false
} else {
default:
return nil, fmt.Errorf("invalid manifest media type: %s", l.MediaType)
}
// find annotation with file name
var digest string
for _, layer := range layers {
if layer.Annotations[TufFileNameAnnotation] == name {
if layer.Annotations[TUFFileNameAnnotation] == name {
digest = layer.Digest
break
}
@@ -267,7 +267,7 @@ func (d *RegistryFetcher) findFileInManifest(mf []byte, name string) (*v1.Hash,
return hash, nil
}
// transportWithTimeout returns a http.RoundTripper with a specified timeout
// transportWithTimeout returns a http.RoundTripper with a specified timeout.
func transportWithTimeout(timeout time.Duration) http.RoundTripper {
// transport is based on go-containerregistry remote.DefaultTransport
// with modifications to include a specified timeout
@@ -286,9 +286,9 @@ func transportWithTimeout(timeout time.Duration) http.RoundTripper {
}
}
// isDelegatedRole returns true if the role is a delegated role
// isDelegatedRole returns true if the role is a delegated role.
func isDelegatedRole(role string) bool {
for _, r := range TufRoles {
for _, r := range Roles {
if role == string(r) {
return false // role is not a delegated role
}
@@ -296,7 +296,7 @@ func isDelegatedRole(role string) bool {
return true // role is a delegated role
}
// roleFromConsistentName returns the role name from a consistent snapshot file name
// roleFromConsistentName returns the role name from a consistent snapshot file name.
func roleFromConsistentName(filename string) string {
name := strings.TrimSuffix(filename, ".json")
role := strings.Split(name, ".")

View File

@@ -30,6 +30,8 @@ import (
const (
tufTargetMediaType = "application/vnd.tuf.target"
testRole = "test-role"
tufMetadataRepo = "tuf-metadata"
)
func TestRegistryFetcher(t *testing.T) {
@@ -40,13 +42,13 @@ func TestRegistryFetcher(t *testing.T) {
t.Fatalf("failed to terminate container: %s", err) // nolint:gocritic
}
}()
LoadRegistryTestData(t, regAddr, OciTufTestDataPath)
LoadRegistryTestData(t, regAddr, OCITUFTestDataPath)
metadataRepo := regAddr.Host + "/tuf-metadata"
metadataImgTag := "latest"
metadataImgTag := LatestTag
targetsRepo := regAddr.Host + "/tuf-targets"
targetFile := "test.txt"
delegatedRole := "test-role"
delegatedRole := testRole
dir := CreateTempDir(t, "", "tuf_temp")
delegatedDir := CreateTempDir(t, dir, delegatedRole)
delegatedTargetFile := fmt.Sprintf("%s/%s", delegatedRole, targetFile)
@@ -126,11 +128,11 @@ func TestFindFileInManifest(t *testing.T) {
img = mutate.ConfigMediaType(img, types.OCIConfigJSON)
// add test layer
name := strings.Join([]string{hash.Hex, file}, ".")
ann := map[string]string{TufFileNameAnnotation: name}
ann := map[string]string{TUFFileNameAnnotation: name}
layer := mutate.Addendum{Layer: static.NewLayer(data, tufTargetMediaType), Annotations: ann}
img, err := mutate.Append(img, layer)
assert.NoError(t, err)
image_manifest, err := img.RawManifest()
imageManifest, err := img.RawManifest()
assert.NoError(t, err)
// make test index manifest
@@ -141,11 +143,11 @@ func TestFindFileInManifest(t *testing.T) {
Add: img,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
TufFileNameAnnotation: name,
TUFFileNameAnnotation: name,
},
},
})
index_manifest, err := idx.RawManifest()
indexManifest, err := idx.RawManifest()
assert.NoError(t, err)
// cache image layer
targetsRepo := "test/tuf-targets"
@@ -155,7 +157,7 @@ func TestFindFileInManifest(t *testing.T) {
}
imgHash, err := img.Digest()
assert.NoError(t, err)
d.cache.Put(fmt.Sprintf("%s@%s", targetsRepo, imgHash.String()), image_manifest)
d.cache.Put(fmt.Sprintf("%s@%s", targetsRepo, imgHash.String()), imageManifest)
testCases := []struct {
name string
@@ -163,9 +165,9 @@ func TestFindFileInManifest(t *testing.T) {
file string
expected string
}{
{"consistent filename image", image_manifest, fmt.Sprintf("%s.%s", hash.Hex, file), hash.Hex},
{"filename image", image_manifest, file, ""},
{"consistent filename index", index_manifest, fmt.Sprintf("%s.%s", hash.Hex, file), hash.Hex},
{"consistent filename image", imageManifest, fmt.Sprintf("%s.%s", hash.Hex, file), hash.Hex},
{"filename image", imageManifest, file, ""},
{"consistent filename index", indexManifest, fmt.Sprintf("%s.%s", hash.Hex, file), hash.Hex},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
@@ -182,9 +184,9 @@ func TestFindFileInManifest(t *testing.T) {
func TestParseImgRef(t *testing.T) {
metadataRepo := "test/tuf-metadata"
metadataTag := "latest"
metadataTag := LatestTag
targetsRepo := "test/tuf-targets"
delegatedRole := "test-role"
delegatedRole := testRole
testCases := []struct {
name string
ref string
@@ -200,7 +202,7 @@ func TestParseImgRef(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
d := &RegistryFetcher{
metadataRepo: metadataRepo,
metadataTag: "latest",
metadataTag: LatestTag,
targetsRepo: targetsRepo,
}
imgRef, file, err := d.parseImgRef(tc.ref)
@@ -246,7 +248,7 @@ func TestPullFileLayer(t *testing.T) {
}()
// make test layer
repo := "tuf-metadata"
repo := tufMetadataRepo
data := []byte("test")
testLayer := static.NewLayer(data, tufTargetMediaType)
hash, err := testLayer.Digest()
@@ -303,7 +305,7 @@ func TestGetManifest(t *testing.T) {
}()
// make test manifest
repo := "tuf-metadata"
repo := tufMetadataRepo
img := empty.Image
img = mutate.MediaType(img, types.OCIManifestSchema1)
img = mutate.ConfigMediaType(img, types.OCIConfigJSON)
@@ -339,7 +341,7 @@ func TestGetManifest(t *testing.T) {
}
}
// RunTestRegistry starts a registry testcontainer for TUF on OCI testdata
// RunTestRegistry starts a registry testcontainer for TUF on OCI testdata.
func RunTestRegistry(t *testing.T) (*registry.RegistryContainer, *url.URL) {
registryContainer, err := registry.Run(context.Background(), "registry:2.8.3")
if err != nil {
@@ -359,22 +361,21 @@ func RunTestRegistry(t *testing.T) (*registry.RegistryContainer, *url.URL) {
return registryContainer, addr
}
// LoadRegistryTestData pushes TUF metadata and targets to an OCI registry
// LoadRegistryTestData pushes TUF metadata and targets to an OCI registry.
func LoadRegistryTestData(t *testing.T, registry *url.URL, path string) {
// push tuf metadata and targets to local registry
METADATA_REPO := "tuf-metadata"
METADATA_TAG := "latest"
TARGETS_REPO := "tuf-targets"
DELEGATED_ROLE := "test-role"
MetadataRepo := tufMetadataRepo
TargetsRepo := "tuf-targets"
DelegatedRole := testRole
// push top-level metadata -> metadata:latest
err := LoadMetadata(filepath.Join(path, "metadata"), registry.Host, METADATA_REPO, METADATA_TAG)
err := LoadMetadata(filepath.Join(path, "metadata"), registry.Host, MetadataRepo, LatestTag)
if err != nil {
t.Fatal(err)
}
// push delegated metadata -> metadata:<DELEGATED_ROLE>
err = LoadMetadata(filepath.Join(path, "metadata", DELEGATED_ROLE), registry.Host, METADATA_REPO, DELEGATED_ROLE)
err = LoadMetadata(filepath.Join(path, "metadata", DelegatedRole), registry.Host, MetadataRepo, DelegatedRole)
if err != nil {
t.Fatal(err)
}
@@ -392,7 +393,7 @@ func LoadRegistryTestData(t *testing.T, registry *url.URL, path string) {
if err != nil {
t.Fatal(err)
}
ref, err := name.ParseReference(fmt.Sprintf("%s/%s:%s", registry.Host, TARGETS_REPO, dir.Name()))
ref, err := name.ParseReference(fmt.Sprintf("%s/%s:%s", registry.Host, TargetsRepo, dir.Name()))
if err != nil {
t.Fatal(err)
}
@@ -400,7 +401,8 @@ func LoadRegistryTestData(t *testing.T, registry *url.URL, path string) {
if err != nil {
t.Fatal(err)
}
if len(mf.Manifests) == 1 {
switch len(mf.Manifests) {
case 1:
// top-level target
img, err := tIdx.Image(mf.Manifests[0].Digest)
if err != nil {
@@ -410,19 +412,19 @@ func LoadRegistryTestData(t *testing.T, registry *url.URL, path string) {
if err != nil {
t.Fatal(err)
}
} else if len(mf.Manifests) > 1 {
case 2:
// delegated target
err = remote.WriteIndex(ref, tIdx, oci.MultiKeychainOption())
if err != nil {
t.Fatal(err)
}
} else {
default:
t.Fatal("no manifests found")
}
}
}
// LoadMetadata loads TUF metadata from a local path and pushes to a registry
// LoadMetadata loads TUF metadata from a local path and pushes to a registry.
func LoadMetadata(path, host, repo, tag string) error {
mIdx, err := layout.ImageIndexFromPath(path)
if err != nil {

View File

@@ -20,36 +20,37 @@ import (
"github.com/theupdateframework/go-tuf/v2/metadata/updater"
)
type TufSource string
type Source string
const (
HttpSource TufSource = "http"
OciSource TufSource = "oci"
HTTPSource Source = "http"
OCISource Source = "oci"
LatestTag string = "latest"
)
var (
DockerTufRootProd = embed.RootProd
DockerTufRootStaging = embed.RootStaging
DockerTufRootDev = embed.RootDev
DockerTufRootDefault = embed.RootDefault
DockerTUFRootProd = embed.RootProd
DockerTUFRootStaging = embed.RootStaging
DockerTUFRootDev = embed.RootDev
DockerTUFRootDefault = embed.RootDefault
)
type TUFClient interface {
type Downloader interface {
DownloadTarget(target, filePath string) (actualFilePath string, data []byte, err error)
}
type TufClient struct {
type Client struct {
updater *updater.Updater
cfg *config.UpdaterConfig
}
// NewTufClient creates a new TUF client
func NewTufClient(initialRoot []byte, tufPath, metadataSource, targetsSource string, versionChecker VersionChecker) (*TufClient, error) {
var tufSource TufSource
// NewClient creates a new TUF client.
func NewClient(initialRoot []byte, tufPath, metadataSource, targetsSource string, versionChecker VersionChecker) (*Client, error) {
var tufSource Source
if strings.HasPrefix(metadataSource, "https://") || strings.HasPrefix(metadataSource, "http://") {
tufSource = HttpSource
tufSource = HTTPSource
} else {
tufSource = OciSource
tufSource = OCISource
}
tufRootDigest := util.SHA256Hex(initialRoot)
@@ -84,11 +85,11 @@ func NewTufClient(initialRoot []byte, tufPath, metadataSource, targetsSource str
cfg.LocalTargetsDir = filepath.Join(metadataPath, "download")
cfg.RemoteTargetsURL = targetsSource
if tufSource == OciSource {
if tufSource == OCISource {
metadataRepo, metadataTag, found := strings.Cut(metadataSource, ":")
if !found {
fmt.Printf("metadata tag not found in URL, using latest\n")
metadataTag = "latest"
metadataTag = LatestTag
}
cfg.Fetcher = NewRegistryFetcher(metadataRepo, metadataTag, targetsSource)
}
@@ -105,7 +106,7 @@ func NewTufClient(initialRoot []byte, tufPath, metadataSource, targetsSource str
return nil, fmt.Errorf("failed to refresh trusted metadata: %w", err)
}
client := &TufClient{
client := &Client{
updater: up,
cfg: cfg,
}
@@ -121,7 +122,7 @@ func NewTufClient(initialRoot []byte, tufPath, metadataSource, targetsSource str
// DownloadTarget downloads the target file using Updater. The Updater gets the target
// information, verifies if the target is already cached, and if it is not cached,
// downloads the target file.
func (t *TufClient) DownloadTarget(target string, filePath string) (actualFilePath string, data []byte, err error) {
func (t *Client) DownloadTarget(target string, filePath string) (actualFilePath string, data []byte, err error) {
// search if the desired target is available
targetInfo, err := t.updater.GetTargetInfo(target)
if err != nil {
@@ -154,15 +155,15 @@ func (t *TufClient) DownloadTarget(target string, filePath string) (actualFilePa
return actualFilePath, data, err
}
func (t *TufClient) GetMetadata() trustedmetadata.TrustedMetadata {
func (t *Client) GetMetadata() trustedmetadata.TrustedMetadata {
return t.updater.GetTrustedMetadataSet()
}
func (t *TufClient) MaxRootLength() int64 {
func (t *Client) MaxRootLength() int64 {
return t.cfg.RootMaxLength
}
func (t *TufClient) GetPriorRoots(metadataURL string) (map[string][]byte, error) {
func (t *Client) GetPriorRoots(metadataURL string) (map[string][]byte, error) {
rootMetadata := map[string][]byte{}
trustedMetadata := t.GetMetadata()
client := fetcher.DefaultFetcher{}
@@ -176,12 +177,12 @@ func (t *TufClient) GetPriorRoots(metadataURL string) (map[string][]byte, error)
return rootMetadata, nil
}
func (t *TufClient) SetRemoteTargetsURL(url string) {
func (t *Client) SetRemoteTargetsURL(url string) {
t.cfg.RemoteTargetsURL = url
}
// Derived from updater.loadTargets() in theupdateframework/go-tuf
func (t *TufClient) LoadDelegatedTargets(roleName, parentName string) (*metadata.Metadata[metadata.TargetsType], error) {
// Derived from updater.loadTargets() in theupdateframework/go-tuf.
func (t *Client) LoadDelegatedTargets(roleName, parentName string) (*metadata.Metadata[metadata.TargetsType], error) {
// extract the targets meta from the trusted snapshot metadata
meta := t.updater.GetTrustedMetadataSet()
metaInfo := meta.Snapshot.Signed.Meta[fmt.Sprintf("%s.json", roleName)]
@@ -208,8 +209,8 @@ func (t *TufClient) LoadDelegatedTargets(roleName, parentName string) (*metadata
return delegatedTargets, nil
}
// downloadMetadata download a metadata file and return it as bytes
func (t *TufClient) downloadMetadata(roleName string, length int64, version string) ([]byte, error) {
// downloadMetadata download a metadata file and return it as bytes.
func (t *Client) downloadMetadata(roleName string, length int64, version string) ([]byte, error) {
urlPath := ensureTrailingSlash(t.cfg.RemoteMetadataURL)
// build urlPath
if version == "" {
@@ -220,7 +221,7 @@ func (t *TufClient) downloadMetadata(roleName string, length int64, version stri
return t.cfg.Fetcher.DownloadFile(urlPath, length, time.Second*15)
}
// ensureTrailingSlash ensures url ends with a slash
// ensureTrailingSlash ensures url ends with a slash.
func ensureTrailingSlash(url string) string {
if updater.IsWindowsPath(url) {
slash := string(filepath.Separator)
@@ -235,7 +236,7 @@ func ensureTrailingSlash(url string) string {
return url + "/"
}
// GetEmbeddedTufRoot returns the embedded TUF root based on the given root name
func GetEmbeddedTufRoot(root string) (*embed.EmbeddedRoot, error) {
// GetEmbeddedRoot returns the embedded TUF root based on the given root name.
func GetEmbeddedRoot(root string) (*embed.EmbeddedRoot, error) {
return embed.GetRootFromName(root)
}

View File

@@ -15,8 +15,8 @@ import (
)
var (
HttpTufTestDataPath = filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo")
OciTufTestDataPath = filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo-oci")
HTTPTUFTestDataPath = filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo")
OCITUFTestDataPath = filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo-oci")
)
func CreateTempDir(t *testing.T, dir, pattern string) string {
@@ -35,12 +35,12 @@ func CreateTempDir(t *testing.T, dir, pattern string) string {
return tempDir
}
// NewTufClient creates a new TUF client
// NewTufClient creates a new TUF client.
func TestRootInit(t *testing.T) {
tufPath := CreateTempDir(t, "", "tuf_temp")
// Start a test HTTP server to serve data from /test/testdata/tuf/test-repo/ paths
server := httptest.NewServer(http.FileServer(http.Dir(HttpTufTestDataPath)))
server := httptest.NewServer(http.FileServer(http.Dir(HTTPTUFTestDataPath)))
defer server.Close()
// run local registry
@@ -50,10 +50,10 @@ func TestRootInit(t *testing.T) {
t.Fatalf("failed to terminate container: %s", err) // nolint:gocritic
}
}()
LoadRegistryTestData(t, regAddr, OciTufTestDataPath)
LoadRegistryTestData(t, regAddr, OCITUFTestDataPath)
alwaysGoodVersionChecker := &mockVersionChecker{err: nil}
alwaysBadVersionChecker := &mockVersionChecker{err: assert.AnError}
alwaysGoodVersionChecker := &MockVersionChecker{err: nil}
alwaysBadVersionChecker := &MockVersionChecker{err: assert.AnError}
testCases := []struct {
name string
@@ -65,17 +65,17 @@ func TestRootInit(t *testing.T) {
}
for _, tc := range testCases {
_, err := NewTufClient(embed.RootDev.Data, tufPath, tc.metadataSource, tc.targetsSource, alwaysGoodVersionChecker)
_, err := NewClient(embed.RootDev.Data, tufPath, tc.metadataSource, tc.targetsSource, alwaysGoodVersionChecker)
assert.NoErrorf(t, err, "Failed to create TUF client: %v", err)
// recreation should work with same root
_, err = NewTufClient(embed.RootDev.Data, tufPath, tc.metadataSource, tc.targetsSource, alwaysGoodVersionChecker)
_, err = NewClient(embed.RootDev.Data, tufPath, tc.metadataSource, tc.targetsSource, alwaysGoodVersionChecker)
assert.NoErrorf(t, err, "Failed to recreate TUF client: %v", err)
_, err = NewTufClient([]byte("broken"), tufPath, tc.metadataSource, tc.targetsSource, alwaysGoodVersionChecker)
_, err = NewClient([]byte("broken"), tufPath, tc.metadataSource, tc.targetsSource, alwaysGoodVersionChecker)
assert.Errorf(t, err, "Expected error recreating TUF client with broken root: %v", err)
_, err = NewTufClient(embed.RootDev.Data, tufPath, tc.metadataSource, tc.targetsSource, alwaysBadVersionChecker)
_, err = NewClient(embed.RootDev.Data, tufPath, tc.metadataSource, tc.targetsSource, alwaysBadVersionChecker)
assert.Errorf(t, err, "Expected error creating TUF client with bad attest version: %v", err)
}
}
@@ -83,11 +83,11 @@ func TestRootInit(t *testing.T) {
func TestDownloadTarget(t *testing.T) {
tufPath := CreateTempDir(t, "", "tuf_temp")
targetFile := "test.txt"
delegatedRole := "test-role"
delegatedRole := testRole
delegatedTargetFile := fmt.Sprintf("%s/%s", delegatedRole, targetFile)
// Start a test HTTP server to serve data from /test/testdata/tuf/test-repo/ paths
server := httptest.NewServer(http.FileServer(http.Dir(HttpTufTestDataPath)))
server := httptest.NewServer(http.FileServer(http.Dir(HTTPTUFTestDataPath)))
defer server.Close()
// run local registry
@@ -97,9 +97,9 @@ func TestDownloadTarget(t *testing.T) {
t.Fatalf("failed to terminate container: %s", err) // nolint:gocritic
}
}()
LoadRegistryTestData(t, regAddr, OciTufTestDataPath)
LoadRegistryTestData(t, regAddr, OCITUFTestDataPath)
alwaysGoodVersionChecker := &mockVersionChecker{err: nil}
alwaysGoodVersionChecker := &MockVersionChecker{err: nil}
testCases := []struct {
name string
@@ -111,7 +111,7 @@ func TestDownloadTarget(t *testing.T) {
}
for _, tc := range testCases {
tufClient, err := NewTufClient(embed.RootDev.Data, tufPath, tc.metadataSource, tc.targetsSource, alwaysGoodVersionChecker)
tufClient, err := NewClient(embed.RootDev.Data, tufPath, tc.metadataSource, tc.targetsSource, alwaysGoodVersionChecker)
assert.NoErrorf(t, err, "Failed to create TUF client: %v", err)
// get trusted tuf metadata
@@ -135,22 +135,22 @@ func TestDownloadTarget(t *testing.T) {
}
func TestGetEmbeddedTufRootBytes(t *testing.T) {
dev, err := GetEmbeddedTufRoot("dev")
dev, err := GetEmbeddedRoot("dev")
assert.NoError(t, err)
staging, err := GetEmbeddedTufRoot("staging")
staging, err := GetEmbeddedRoot("staging")
assert.NoError(t, err)
assert.NotEqual(t, dev.Data, staging.Data)
prod, err := GetEmbeddedTufRoot("prod")
prod, err := GetEmbeddedRoot("prod")
assert.NoError(t, err)
assert.NotEqual(t, dev.Data, prod.Data)
assert.NotEqual(t, staging.Data, prod.Data)
def, err := GetEmbeddedTufRoot("")
def, err := GetEmbeddedRoot("")
assert.NoError(t, err)
assert.Equal(t, def.Data, prod.Data)
_, err = GetEmbeddedTufRoot("invalid")
_, err = GetEmbeddedRoot("invalid")
assert.Error(t, err)
}

View File

@@ -12,7 +12,7 @@ const ThisModulePath = "github.com/docker/attest"
type VersionChecker interface {
// CheckVersion checks if the current version of this library meets the constraints from the TUF repo
CheckVersion(tufClient TUFClient) error
CheckVersion(tufClient Downloader) error
}
type InvalidVersionError struct {
@@ -32,13 +32,13 @@ func (e *InvalidVersionError) Error() string {
return fmt.Sprintf("%s version %s does not satisfy constraints %s: %s", ThisModulePath, e.AttestVersion, e.VersionConstraint, errsStr.String())
}
func NewVersionChecker() *versionChecker {
return &versionChecker{}
func NewDefaultVersionChecker() *DefaultVersionChecker {
return &DefaultVersionChecker{}
}
type versionChecker struct{}
type DefaultVersionChecker struct{}
func (vc *versionChecker) CheckVersion(client TUFClient) error {
func (vc *DefaultVersionChecker) CheckVersion(client Downloader) error {
var attestMod *debug.Module
bi, ok := debug.ReadBuildInfo()
if !ok {