@@ -20,23 +20,52 @@ use std::cmp::PartialEq;
20
20
use crate :: crypto:: { CosignVerificationKey , Signature } ;
21
21
use crate :: errors:: { Result , SigstoreError } ;
22
22
23
+ #[ derive( Serialize , Deserialize , Debug , PartialEq , Eq ) ]
24
+ pub struct Bundle {
25
+ #[ serde( rename( deserialize = "base64Signature" ) ) ]
26
+ pub base64_signature : String ,
27
+ pub cert : String ,
28
+ #[ serde( rename( deserialize = "rekorBundle" ) ) ]
29
+ pub rekor_bundle : RekorBundle ,
30
+ }
31
+
32
+ impl Bundle {
33
+ #[ allow( dead_code) ]
34
+ pub ( crate ) fn new_verified ( raw : & str , rekor_pub_key : & CosignVerificationKey ) -> Result < Self > {
35
+ let bundle: Bundle = serde_json:: from_str ( raw) . map_err ( |e| {
36
+ SigstoreError :: UnexpectedError ( format ! ( "Cannot parse bundle |{}|: {:?}" , raw, e) )
37
+ } ) ?;
38
+ RekorBundle :: verify_bundle ( & bundle. rekor_bundle , rekor_pub_key) . map ( |_| bundle)
39
+ }
40
+ }
41
+
23
42
#[ derive( Serialize , Deserialize , Debug , Clone , PartialEq , Eq ) ]
24
43
#[ serde( rename_all = "PascalCase" ) ]
25
- pub struct Bundle {
44
+ pub struct RekorBundle {
26
45
pub signed_entry_timestamp : String ,
27
46
pub payload : Payload ,
28
47
}
29
48
30
- impl Bundle {
31
- /// Create a new verified `Bundle `
49
+ impl RekorBundle {
50
+ /// Create a new verified `RekorBundle `
32
51
///
33
52
/// **Note well:** The bundle will be returned only if it can be verified
34
53
/// using the supplied `rekor_pub_key` public key.
35
54
pub ( crate ) fn new_verified ( raw : & str , rekor_pub_key : & CosignVerificationKey ) -> Result < Self > {
36
- let bundle: Bundle = serde_json:: from_str ( raw) . map_err ( |e| {
55
+ let bundle: RekorBundle = serde_json:: from_str ( raw) . map_err ( |e| {
37
56
SigstoreError :: UnexpectedError ( format ! ( "Cannot parse bundle |{}|: {:?}" , raw, e) )
38
57
} ) ?;
58
+ Self :: verify_bundle ( & bundle, rekor_pub_key) . map ( |_| bundle)
59
+ }
39
60
61
+ /// Verify a `RekorBundle`.
62
+ ///
63
+ /// **Note well:** The bundle will be returned only if it can be verified
64
+ /// using the supplied `rekor_pub_key` public key.
65
+ pub ( crate ) fn verify_bundle (
66
+ bundle : & RekorBundle ,
67
+ rekor_pub_key : & CosignVerificationKey ,
68
+ ) -> Result < ( ) > {
40
69
let mut buf = Vec :: new ( ) ;
41
70
let mut ser = serde_json:: Serializer :: with_formatter ( & mut buf, CanonicalFormatter :: new ( ) ) ;
42
71
bundle. payload . serialize ( & mut ser) . map_err ( |e| {
@@ -50,7 +79,7 @@ impl Bundle {
50
79
Signature :: Base64Encoded ( bundle. signed_entry_timestamp . as_bytes ( ) ) ,
51
80
& buf,
52
81
) ?;
53
- Ok ( bundle )
82
+ Ok ( ( ) )
54
83
}
55
84
}
56
85
@@ -72,7 +101,7 @@ mod tests {
72
101
use crate :: cosign:: tests:: get_rekor_public_key;
73
102
use crate :: crypto:: SigningScheme ;
74
103
75
- fn build_correct_bundle ( ) -> String {
104
+ fn build_correct_rekor_bundle ( ) -> String {
76
105
let bundle_json = json ! ( {
77
106
"SignedEntryTimestamp" : "MEUCIDx9M+yRpD0O47/Mzm8NAPCbtqy4uiTkLWWexW0bo4jZAiEA1wwueIW8XzJWNkut5y9snYj7UOfbMmUXp7fH3CzJmWg=" ,
78
107
"Payload" : {
@@ -86,17 +115,17 @@ mod tests {
86
115
}
87
116
88
117
#[ test]
89
- fn bundle_new_verified_success ( ) {
118
+ fn rekor_bundle_new_verified_success ( ) {
90
119
let rekor_pub_key = get_rekor_public_key ( ) ;
91
120
92
- let bundle_json = build_correct_bundle ( ) ;
93
- let bundle = Bundle :: new_verified ( & bundle_json, & rekor_pub_key) ;
121
+ let bundle_json = build_correct_rekor_bundle ( ) ;
122
+ let bundle = RekorBundle :: new_verified ( & bundle_json, & rekor_pub_key) ;
94
123
95
124
assert ! ( bundle. is_ok( ) ) ;
96
125
}
97
126
98
127
#[ test]
99
- fn bundle_new_verified_failure ( ) {
128
+ fn rekor_bundle_new_verified_failure ( ) {
100
129
let public_key = r#"-----BEGIN PUBLIC KEY-----
101
130
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENptdY/l3nB0yqkXLBWkZWQwo6+cu
102
131
OSWS1X9vPavpiQOoTTGC0xX57OojUadxF1cdQmrsiReWg2Wn4FneJfa8xw==
@@ -105,9 +134,24 @@ OSWS1X9vPavpiQOoTTGC0xX57OojUadxF1cdQmrsiReWg2Wn4FneJfa8xw==
105
134
CosignVerificationKey :: from_pem ( public_key. as_bytes ( ) , & SigningScheme :: default ( ) )
106
135
. expect ( "Cannot create CosignVerificationKey" ) ;
107
136
108
- let bundle_json = build_correct_bundle ( ) ;
109
- let bundle = Bundle :: new_verified ( & bundle_json, & not_rekor_pub_key) ;
137
+ let bundle_json = build_correct_rekor_bundle ( ) ;
138
+ let bundle = RekorBundle :: new_verified ( & bundle_json, & not_rekor_pub_key) ;
110
139
111
140
assert ! ( bundle. is_err( ) ) ;
112
141
}
142
+
143
+ #[ test]
144
+ fn bundle_new_verified_success ( ) {
145
+ // Bundle as generated by running the following command, and taking the
146
+ // content from the generated 'artifact.bundle` file:
147
+ // cosign sign-blob --bundle=artifact.bundle artifact.txt
148
+ let bundle_raw = r#"
149
+ {"base64Signature":"MEQCIGp1XZP5zaImosrBhDPCdXn3f8xI9FHGLsGVx6UeRPCgAiAt5GrsdQhOKnZcA3EWecvgJSHzCIjWifFBQkD7Hdsymg==","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQWkrZ0F3SUJBZ0lVVFBXVGZPLzFOUmFTRmRlY2FBUS9wQkRHSnA4d0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpJeE1USTFNRGN6TnpFeVdoY05Nakl4TVRJMU1EYzBOekV5V2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVKUVE0Vy81WFA5bTRZYldSQlF0SEdXd245dVVoYWUzOFVwY0oKcEVNM0RPczR6VzRNSXJNZlc0V1FEMGZ3cDhQVVVSRFh2UTM5NHBvcWdHRW1Ta3J1THFPQ0FVNHdnZ0ZLTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVvM0tuCmpKUVowWGZpZ2JENWIwT1ZOTjB4cVNvd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0p3WURWUjBSQVFIL0JCMHdHNEVaWkdGdWFXVnNMbUpsZG1WdWFYVnpRR2R0WVdsc0xtTnZiVEFzQmdvcgpCZ0VFQVlPL01BRUJCQjVvZEhSd2N6b3ZMMmRwZEdoMVlpNWpiMjB2Ykc5bmFXNHZiMkYxZEdnd2dZc0dDaXNHCkFRUUIxbmtDQkFJRWZRUjdBSGtBZHdEZFBUQnF4c2NSTW1NWkhoeVpaemNDb2twZXVONDhyZitIaW5LQUx5bnUKamdBQUFZU3R1Qkh5QUFBRUF3QklNRVlDSVFETTVZU1EvR0w2S0k1UjlPZGNuL3BTaytxVkQ2YnNMODMrRXA5UgoyaFdUYXdJaEFLMWppMWxaNTZEc2Z1TGZYN2JCQzluYlIzRWx4YWxCaHYxelFYTVU3dGx3TUFvR0NDcUdTTTQ5CkJBTURBMmNBTUdRQ01CSzh0c2dIZWd1aCtZaGVsM1BpakhRbHlKMVE1SzY0cDB4cURkbzdXNGZ4Zm9BUzl4clAKczJQS1FjZG9EOWJYd2dJd1g2ekxqeWJaa05IUDV4dEJwN3ZLMkZZZVp0ME9XTFJsVWxsY1VETDNULzdKUWZ3YwpHU3E2dlZCTndKMDB3OUhSCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K","rekorBundle":{"SignedEntryTimestamp":"MEUCIC3c+21v9pk6o4BpB/dRAM9lGnyWLi3Xnc+i8LmnNJmeAiEAiqZJbZHx3Idnw+zXv6yM0ipPw/p16R28YGuCJFQ1u8U=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI0YmM0NTNiNTNjYjNkOTE0YjQ1ZjRiMjUwMjk0MjM2YWRiYTJjMGUwOWZmNmYwMzc5Mzk0OWU3ZTM5ZmQ0Y2MxIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJR3AxWFpQNXphSW1vc3JCaERQQ2RYbjNmOHhJOUZIR0xzR1Z4NlVlUlBDZ0FpQXQ1R3JzZFFoT0tuWmNBM0VXZWN2Z0pTSHpDSWpXaWZGQlFrRDdIZHN5bWc9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnhSRU5EUVdrclowRjNTVUpCWjBsVlZGQlhWR1pQTHpGT1VtRlRSbVJsWTJGQlVTOXdRa1JIU25BNGQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVU1RGTlJHTjZUbnBGZVZkb1kwNU5ha2w0VFZSSk1VMUVZekJPZWtWNVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZLVVZFMFZ5ODFXRkE1YlRSWllsZFNRbEYwU0VkWGQyNDVkVlZvWVdVek9GVndZMG9LY0VWTk0wUlBjelI2VnpSTlNYSk5abGMwVjFGRU1HWjNjRGhRVlZWU1JGaDJVVE01TkhCdmNXZEhSVzFUYTNKMVRIRlBRMEZWTkhkblowWkxUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZ2TTB0dUNtcEtVVm93V0dacFoySkVOV0l3VDFaT1RqQjRjVk52ZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBwM1dVUldVakJTUVZGSUwwSkNNSGRITkVWYVdrZEdkV0ZYVm5OTWJVcHNaRzFXZFdGWVZucFJSMlIwV1Zkc2MweHRUblppVkVGelFtZHZjZ3BDWjBWRlFWbFBMMDFCUlVKQ1FqVnZaRWhTZDJONmIzWk1NbVJ3WkVkb01WbHBOV3BpTWpCMllrYzVibUZYTkhaaU1rWXhaRWRuZDJkWmMwZERhWE5IQ2tGUlVVSXhibXREUWtGSlJXWlJVamRCU0d0QlpIZEVaRkJVUW5GNGMyTlNUVzFOV2tob2VWcGFlbU5EYjJ0d1pYVk9ORGh5Wml0SWFXNUxRVXg1Ym5VS2FtZEJRVUZaVTNSMVFraDVRVUZCUlVGM1FrbE5SVmxEU1ZGRVRUVlpVMUV2UjB3MlMwazFVamxQWkdOdUwzQlRheXR4VmtRMlluTk1PRE1yUlhBNVVnb3lhRmRVWVhkSmFFRkxNV3BwTVd4YU5UWkVjMloxVEdaWU4ySkNRemx1WWxJelJXeDRZV3hDYUhZeGVsRllUVlUzZEd4M1RVRnZSME5EY1VkVFRUUTVDa0pCVFVSQk1tTkJUVWRSUTAxQ1N6aDBjMmRJWldkMWFDdFphR1ZzTTFCcGFraFJiSGxLTVZFMVN6WTBjREI0Y1VSa2J6ZFhOR1o0Wm05QlV6bDRjbEFLY3pKUVMxRmpaRzlFT1dKWWQyZEpkMWcyZWt4cWVXSmFhMDVJVURWNGRFSndOM1pMTWtaWlpWcDBNRTlYVEZKc1ZXeHNZMVZFVEROVUx6ZEtVV1ozWXdwSFUzRTJkbFpDVG5kS01EQjNPVWhTQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0=","integratedTime":1669361833,"logIndex":7810348,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}
150
+ "# ;
151
+ let rekor_pub_key = get_rekor_public_key ( ) ;
152
+ let result = Bundle :: new_verified ( & bundle_raw, & rekor_pub_key) ;
153
+ assert ! ( result. is_ok( ) ) ;
154
+ let bundle = result. unwrap ( ) ;
155
+ assert_eq ! ( bundle. rekor_bundle. payload. log_index, 7810348 ) ;
156
+ }
113
157
}
0 commit comments