@@ -54,6 +54,7 @@ import (
5454 "github.com/sigstore/cosign/v3/pkg/oci/static"
5555 cosigntypes "github.com/sigstore/cosign/v3/pkg/types"
5656 rc "github.com/sigstore/rekor/pkg/client"
57+ "github.com/sigstore/rekor/pkg/generated/models"
5758 "github.com/sigstore/sigstore/pkg/cryptoutils"
5859 "github.com/sigstore/sigstore/pkg/signature"
5960 "gopkg.in/go-jose/go-jose.v2/json"
@@ -208,7 +209,7 @@ func createSignatureData(ctx context.Context, imageName string, digestImage name
208209 }
209210
210211 // Upload to transparency log to get bundle information like Tekton Chains does
211- rekorBundle , err := uploadToTransparencyLog (ctx , payload , rawSignature , signer )
212+ rekorBundle , _ , err := uploadToTransparencyLog (ctx , payload , rawSignature , signer )
212213 if err != nil {
213214 return nil , err
214215 }
@@ -300,42 +301,43 @@ func CreateAndPushImageSignature(ctx context.Context, imageName string, keyName
300301}
301302
302303// uploadToTransparencyLog uploads a signature to the transparency log and returns the bundle
303- func uploadToTransparencyLog (ctx context.Context , payload []byte , rawSignature []byte , signer signature.SignerVerifier ) (* bundle.RekorBundle , error ) {
304+ // along with the raw tlog entry (needed for creating protobuf bundles).
305+ func uploadToTransparencyLog (ctx context.Context , payload []byte , rawSignature []byte , signer signature.SignerVerifier ) (* bundle.RekorBundle , * models.LogEntryAnon , error ) {
304306 // Get public key or cert for transparency log upload
305307 pkoc , err := getPublicKeyOrCert (signer )
306308 if err != nil {
307- return nil , fmt .Errorf ("failed to get public key or cert: %w" , err )
309+ return nil , nil , fmt .Errorf ("failed to get public key or cert: %w" , err )
308310 }
309311
310312 // Get Rekor URL
311313 rekorURL , err := rekor .StubRekor (ctx )
312314 if err != nil {
313- return nil , fmt .Errorf ("failed to get stub rekor URL: %w" , err )
315+ return nil , nil , fmt .Errorf ("failed to get stub rekor URL: %w" , err )
314316 }
315317
316318 rekorClient , err := rc .GetRekorClient (rekorURL )
317319 if err != nil {
318- return nil , fmt .Errorf ("failed to get rekor client: %w" , err )
320+ return nil , nil , fmt .Errorf ("failed to get rekor client: %w" , err )
319321 }
320322
321323 // Compute payload checksum
322324 checksum := sha256 .New ()
323325 if _ , err := checksum .Write (payload ); err != nil {
324- return nil , fmt .Errorf ("error checksuming payload: %w" , err )
326+ return nil , nil , fmt .Errorf ("error checksuming payload: %w" , err )
325327 }
326328
327329 tlogEntry , err := cosign .TLogUpload (ctx , rekorClient , rawSignature , checksum , pkoc )
328330 if err != nil {
329- return nil , fmt .Errorf ("failed to upload to transparency log: %w" , err )
331+ return nil , nil , fmt .Errorf ("failed to upload to transparency log: %w" , err )
330332 }
331333
332334 // Create bundle from the actual transparency log entry
333335 rekorBundle := bundle .EntryToBundle (tlogEntry )
334336 if rekorBundle == nil {
335- return nil , fmt .Errorf ("rekorBundle is nil after EntryToBundle" )
337+ return nil , nil , fmt .Errorf ("rekorBundle is nil after EntryToBundle" )
336338 }
337339
338- return rekorBundle , nil
340+ return rekorBundle , tlogEntry , nil
339341}
340342
341343// getImageDigestAndRef returns the image, its digest, and digest reference for signing
@@ -533,7 +535,7 @@ func createAndPushAttestationInternal(ctx context.Context, imageName, keyName st
533535 }
534536
535537 // Upload to transparency log to get bundle information like Tekton Chains does
536- rekorBundle , err := uploadToTransparencyLog (ctx , signedAttestation , rawSignature , signer )
538+ rekorBundle , _ , err := uploadToTransparencyLog (ctx , signedAttestation , rawSignature , signer )
537539 if err != nil {
538540 return ctx , err
539541 }
@@ -670,8 +672,8 @@ func CreateAndPushAttestationReferrer(ctx context.Context, imageName, keyName st
670672 return ctx , fmt .Errorf ("error stubbing rekor endpoints for attestation: %w" , err )
671673 }
672674
673- // Upload to transparency log to get bundle information
674- rekorBundle , err := uploadToTransparencyLog (ctx , signedAttestation , rawSignature , signer )
675+ // Upload to transparency log to get bundle information like Tekton Chains does
676+ rekorBundle , _ , err := uploadToTransparencyLog (ctx , signedAttestation , rawSignature , signer )
675677 if err != nil {
676678 return ctx , err
677679 }
@@ -717,6 +719,111 @@ func CreateAndPushAttestationReferrer(ctx context.Context, imageName, keyName st
717719 return ctx , nil
718720}
719721
722+ // CreateAndPushBundleAttestationReferrer creates a protobuf Sigstore bundle attestation
723+ // and pushes it as an OCI referrer with the bundle media type that cosign recognizes.
724+ func CreateAndPushBundleAttestationReferrer (ctx context.Context , imageName , keyName string ) (context.Context , error ) {
725+ var state * imageState
726+ ctx , err := testenv .SetupState (ctx , & state )
727+ if err != nil {
728+ return ctx , err
729+ }
730+
731+ if state .ReferrerAttestations [imageName ] != "" {
732+ return ctx , nil
733+ }
734+
735+ image , digest , _ , err := getImageDigestAndRef (ctx , imageName )
736+ if err != nil {
737+ return ctx , err
738+ }
739+
740+ statement , err := attestation .CreateStatementFor (imageName , image )
741+ if err != nil {
742+ return ctx , err
743+ }
744+
745+ signedAttestation , err := attestation .SignStatement (ctx , keyName , statement )
746+ if err != nil {
747+ return ctx , err
748+ }
749+
750+ var sig * cosign.Signatures
751+ sig , err = unmarshallSignatures (signedAttestation )
752+ if err != nil {
753+ return ctx , err
754+ }
755+ if sig == nil {
756+ return ctx , fmt .Errorf ("failed to extract signature from attestation: no signatures found" )
757+ }
758+
759+ state .ReferrerAttestationSignatures [imageName ] = Signature {
760+ KeyID : sig .KeyID ,
761+ Signature : sig .Sig ,
762+ }
763+
764+ var rawSignature []byte
765+ if sig .Sig != "" {
766+ rawSignature , err = base64 .StdEncoding .DecodeString (sig .Sig )
767+ if err != nil {
768+ return ctx , fmt .Errorf ("failed to decode signature: %w" , err )
769+ }
770+ }
771+
772+ signer , err := crypto .SignerWithKey (ctx , keyName )
773+ if err != nil {
774+ return ctx , err
775+ }
776+
777+ publicKey , err := signer .PublicKey ()
778+ if err != nil {
779+ return ctx , fmt .Errorf ("failed to get public key: %w" , err )
780+ }
781+
782+ publicKeyBytes , err := cryptoutils .MarshalPublicKeyToPEM (publicKey )
783+ if err != nil {
784+ return ctx , fmt .Errorf ("failed to marshal public key: %w" , err )
785+ }
786+
787+ err = rekor .StubRekorEntryCreationForAttestation (ctx , signedAttestation , publicKeyBytes )
788+ if err != nil {
789+ return ctx , fmt .Errorf ("error stubbing rekor endpoints for attestation: %w" , err )
790+ }
791+
792+ _ , tlogEntry , err := uploadToTransparencyLog (ctx , signedAttestation , rawSignature , signer )
793+ if err != nil {
794+ return ctx , err
795+ }
796+
797+ statementPayload , err := json .Marshal (statement )
798+ if err != nil {
799+ return ctx , fmt .Errorf ("failed to marshal statement: %w" , err )
800+ }
801+
802+ bundleBytes , err := bundle .MakeNewBundle (publicKey , tlogEntry , statementPayload , signedAttestation , publicKeyBytes , nil )
803+ if err != nil {
804+ return ctx , fmt .Errorf ("failed to create protobuf bundle: %w" , err )
805+ }
806+
807+ digestRef , err := getDigestRefForImage (ctx , imageName , digest )
808+ if err != nil {
809+ return ctx , err
810+ }
811+
812+ err = cosignRemote .WriteAttestationNewBundleFormat (
813+ digestRef ,
814+ bundleBytes ,
815+ statement .PredicateType ,
816+ cosignRemote .WithRemoteOptions (remote .WithContext (ctx )),
817+ )
818+ if err != nil {
819+ return ctx , fmt .Errorf ("failed to write attestation bundle referrer: %w" , err )
820+ }
821+
822+ state .ReferrerAttestations [imageName ] = digestRef .String ()
823+
824+ return ctx , nil
825+ }
826+
720827// CreateAndPushV1Attestation for a named image creates a SLSA v1.0 attestation
721828// and pushes it to the stub registry
722829func CreateAndPushV1Attestation (ctx context.Context , imageName , keyName string ) (context.Context , error ) {
@@ -1197,12 +1304,15 @@ func AttestationSignaturesFrom(ctx context.Context, prefix string) (map[string]s
11971304 state := testenv.FetchState [imageState ](ctx )
11981305
11991306 signatures := map [string ]string {}
1200- for name , signature := range state .AttestationSignatures {
1201- if signature .KeyID != "" {
1202- signatures [fmt .Sprintf ("%s_KEY_ID_%s" , prefix , name )] = signature .KeyID
1203- }
1204- if signature .Signature != "" {
1205- signatures [fmt .Sprintf ("%s_%s" , prefix , name )] = signature .Signature
1307+ // Referrer entries first so legacy entries take precedence (overwrite) for same image name
1308+ for _ , m := range []map [string ]Signature {state .ReferrerAttestationSignatures , state .AttestationSignatures } {
1309+ for name , signature := range m {
1310+ if signature .KeyID != "" {
1311+ signatures [fmt .Sprintf ("%s_KEY_ID_%s" , prefix , name )] = signature .KeyID
1312+ }
1313+ if signature .Signature != "" {
1314+ signatures [fmt .Sprintf ("%s_%s" , prefix , name )] = signature .Signature
1315+ }
12061316 }
12071317 }
12081318
@@ -1217,8 +1327,11 @@ func RawAttestationSignaturesFrom(ctx context.Context) map[string]string {
12171327 state := testenv.FetchState [imageState ](ctx )
12181328
12191329 ret := map [string ]string {}
1220- for ref , signature := range state .AttestationSignatures {
1221- ret [fmt .Sprintf ("ATTESTATION_SIGNATURE_%s" , ref )] = signature .Signature
1330+ // Referrer entries first so legacy entries take precedence (overwrite) for same image name
1331+ for _ , m := range []map [string ]Signature {state .ReferrerAttestationSignatures , state .AttestationSignatures } {
1332+ for ref , signature := range m {
1333+ ret [fmt .Sprintf ("ATTESTATION_SIGNATURE_%s" , ref )] = signature .Signature
1334+ }
12221335 }
12231336
12241337 return ret
@@ -1232,12 +1345,15 @@ func ImageSignaturesFrom(ctx context.Context, prefix string) (map[string]string,
12321345 state := testenv.FetchState [imageState ](ctx )
12331346
12341347 ret := map [string ]string {}
1235- for name , signature := range state .ImageSignatures {
1236- if signature .KeyID != "" {
1237- ret [fmt .Sprintf ("%s_KEY_ID_%s" , prefix , name )] = signature .KeyID
1238- }
1239- if signature .Signature != "" {
1240- ret [fmt .Sprintf ("%s_%s" , prefix , name )] = signature .Signature
1348+ // Referrer entries first so legacy entries take precedence (overwrite) for same image name
1349+ for _ , m := range []map [string ]Signature {state .ReferrerImageSignatures , state .ImageSignatures } {
1350+ for name , signature := range m {
1351+ if signature .KeyID != "" {
1352+ ret [fmt .Sprintf ("%s_KEY_ID_%s" , prefix , name )] = signature .KeyID
1353+ }
1354+ if signature .Signature != "" {
1355+ ret [fmt .Sprintf ("%s_%s" , prefix , name )] = signature .Signature
1356+ }
12411357 }
12421358 }
12431359
@@ -1252,8 +1368,11 @@ func RawImageSignaturesFrom(ctx context.Context) map[string]string {
12521368 state := testenv.FetchState [imageState ](ctx )
12531369
12541370 ret := map [string ]string {}
1255- for ref , signature := range state .ImageSignatures {
1256- ret [fmt .Sprintf ("IMAGE_SIGNATURE_%s" , ref )] = signature .Signature
1371+ // Referrer entries first so legacy entries take precedence (overwrite) for same image name
1372+ for _ , m := range []map [string ]Signature {state .ReferrerImageSignatures , state .ImageSignatures } {
1373+ for ref , signature := range m {
1374+ ret [fmt .Sprintf ("IMAGE_SIGNATURE_%s" , ref )] = signature .Signature
1375+ }
12571376 }
12581377
12591378 return ret
@@ -1412,4 +1531,5 @@ func AddStepsTo(sc *godog.ScenarioContext) {
14121531 sc .Step (`^an OCI blob with content "([^"]*)" in the repo "([^"]*)"$` , createAndPushLayer )
14131532 sc .Step (`^a valid image signature referrer of "([^"]*)" image signed by the "([^"]*)" key$` , CreateAndPushImageSignatureReferrer )
14141533 sc .Step (`^a valid attestation referrer of "([^"]*)" signed by the "([^"]*)" key$` , CreateAndPushAttestationReferrer )
1534+ sc .Step (`^a valid bundle-format attestation referrer of "([^"]*)" signed by the "([^"]*)" key$` , CreateAndPushBundleAttestationReferrer )
14151535}
0 commit comments