Skip to content

Commit b45a7c1

Browse files
committed
Rename Bundle to RekorBundle and modify Bundle
This commit renames the Bundle struct to RekorBundle and modifies the existing Bundle struct to contain the rekor_bundle, in addition to a base64_signature, and cert field. The motivation for change comes from trying to implement an example that verifies a blob using a bundle. For example, first a blob is signed using the following command: cosign sign-blob --bundle=artifact.bundle artifact.txt The `artifact.bundle` file generated by the above command will look something like this (shortened to fit the commit message format): { "base64Signature": "...", "cert": "...", "rekorBundle": { "SignedEntryTimestamp": "...", "Payload": { "body": "...", "integratedTime": 1669361833, "logIndex": 7810348, "logID": "..." } } } Currently, to create Bundle (which is called RekorBundle in this commit) from this, one would have to parse the string as json, and then access the `rekorBundle` element, and then serialize it so that it can be passed to `Bundle::new_verified` (again RekorBundle in this commit). With the changes in this commit it will be possible to call `Bundle::new_verified` and pass in the contents for the bundle file directly. Refs: sigstore#117 Signed-off-by: Daniel Bevenius <[email protected]>
1 parent 37b9e42 commit b45a7c1

File tree

3 files changed

+72
-26
lines changed

3 files changed

+72
-26
lines changed

src/cosign/bundle.rs

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,52 @@ use std::cmp::PartialEq;
2020
use crate::crypto::{CosignVerificationKey, Signature};
2121
use crate::errors::{Result, SigstoreError};
2222

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+
2342
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
2443
#[serde(rename_all = "PascalCase")]
25-
pub struct Bundle {
44+
pub struct RekorBundle {
2645
pub signed_entry_timestamp: String,
2746
pub payload: Payload,
2847
}
2948

30-
impl Bundle {
31-
/// Create a new verified `Bundle`
49+
impl RekorBundle {
50+
/// Create a new verified `RekorBundle`
3251
///
3352
/// **Note well:** The bundle will be returned only if it can be verified
3453
/// using the supplied `rekor_pub_key` public key.
3554
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| {
3756
SigstoreError::UnexpectedError(format!("Cannot parse bundle |{}|: {:?}", raw, e))
3857
})?;
58+
Self::verify_bundle(&bundle, rekor_pub_key).map(|_| bundle)
59+
}
3960

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<()> {
4069
let mut buf = Vec::new();
4170
let mut ser = serde_json::Serializer::with_formatter(&mut buf, CanonicalFormatter::new());
4271
bundle.payload.serialize(&mut ser).map_err(|e| {
@@ -50,7 +79,7 @@ impl Bundle {
5079
Signature::Base64Encoded(bundle.signed_entry_timestamp.as_bytes()),
5180
&buf,
5281
)?;
53-
Ok(bundle)
82+
Ok(())
5483
}
5584
}
5685

@@ -72,7 +101,7 @@ mod tests {
72101
use crate::cosign::tests::get_rekor_public_key;
73102
use crate::crypto::SigningScheme;
74103

75-
fn build_correct_bundle() -> String {
104+
fn build_correct_rekor_bundle() -> String {
76105
let bundle_json = json!({
77106
"SignedEntryTimestamp": "MEUCIDx9M+yRpD0O47/Mzm8NAPCbtqy4uiTkLWWexW0bo4jZAiEA1wwueIW8XzJWNkut5y9snYj7UOfbMmUXp7fH3CzJmWg=",
78107
"Payload": {
@@ -86,17 +115,17 @@ mod tests {
86115
}
87116

88117
#[test]
89-
fn bundle_new_verified_success() {
118+
fn rekor_bundle_new_verified_success() {
90119
let rekor_pub_key = get_rekor_public_key();
91120

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);
94123

95124
assert!(bundle.is_ok());
96125
}
97126

98127
#[test]
99-
fn bundle_new_verified_failure() {
128+
fn rekor_bundle_new_verified_failure() {
100129
let public_key = r#"-----BEGIN PUBLIC KEY-----
101130
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENptdY/l3nB0yqkXLBWkZWQwo6+cu
102131
OSWS1X9vPavpiQOoTTGC0xX57OojUadxF1cdQmrsiReWg2Wn4FneJfa8xw==
@@ -105,9 +134,24 @@ OSWS1X9vPavpiQOoTTGC0xX57OojUadxF1cdQmrsiReWg2Wn4FneJfa8xw==
105134
CosignVerificationKey::from_pem(public_key.as_bytes(), &SigningScheme::default())
106135
.expect("Cannot create CosignVerificationKey");
107136

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);
110139

111140
assert!(bundle.is_err());
112141
}
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+
}
113157
}

src/cosign/signature_layers.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use x509_parser::{
2323
parse_x509_certificate, pem::parse_x509_pem,
2424
};
2525

26-
use super::bundle::Bundle;
26+
use super::bundle::RekorBundle;
2727
use super::constants::{
2828
SIGSTORE_BUNDLE_ANNOTATION, SIGSTORE_CERT_ANNOTATION, SIGSTORE_GITHUB_WORKFLOW_NAME_OID,
2929
SIGSTORE_GITHUB_WORKFLOW_REF_OID, SIGSTORE_GITHUB_WORKFLOW_REPOSITORY_OID,
@@ -137,7 +137,7 @@ pub struct SignatureLayer {
137137
/// signature time.
138138
pub certificate_signature: Option<CertificateSignature>,
139139
/// The bundle produced by Rekor.
140-
pub bundle: Option<Bundle>,
140+
pub bundle: Option<RekorBundle>,
141141
#[serde(skip_serializing)]
142142
pub signature: Option<String>,
143143
#[serde(skip_serializing)]
@@ -298,10 +298,10 @@ impl SignatureLayer {
298298
fn get_bundle_from_annotations(
299299
annotations: &HashMap<String, String>,
300300
rekor_pub_key: Option<&CosignVerificationKey>,
301-
) -> Result<Option<Bundle>> {
301+
) -> Result<Option<RekorBundle>> {
302302
let bundle = match annotations.get(SIGSTORE_BUNDLE_ANNOTATION) {
303303
Some(value) => match rekor_pub_key {
304-
Some(key) => Some(Bundle::new_verified(value, key)?),
304+
Some(key) => Some(RekorBundle::new_verified(value, key)?),
305305
None => {
306306
info!(bundle = ?value, "Ignoring bundle, rekor public key not provided to verification client");
307307
None
@@ -315,7 +315,7 @@ impl SignatureLayer {
315315
fn get_certificate_signature_from_annotations(
316316
annotations: &HashMap<String, String>,
317317
fulcio_cert_pool: Option<&CertificatePool>,
318-
bundle: Option<&Bundle>,
318+
bundle: Option<&RekorBundle>,
319319
) -> Option<CertificateSignature> {
320320
let cert_raw = match annotations.get(SIGSTORE_CERT_ANNOTATION) {
321321
Some(value) => value,
@@ -427,7 +427,7 @@ impl CertificateSignature {
427427
pub(crate) fn from_certificate(
428428
cert_raw: &[u8],
429429
fulcio_cert_pool: &CertificatePool,
430-
trusted_bundle: &Bundle,
430+
trusted_bundle: &RekorBundle,
431431
) -> Result<Self> {
432432
let (_, pem) = parse_x509_pem(cert_raw)?;
433433
let (_, cert) = parse_x509_certificate(&pem.contents)?;
@@ -573,7 +573,7 @@ OSWS1X9vPavpiQOoTTGC0xX57OojUadxF1cdQmrsiReWg2Wn4FneJfa8xw==
573573
)
574574
}
575575

576-
pub(crate) fn build_bundle() -> Bundle {
576+
pub(crate) fn build_bundle() -> RekorBundle {
577577
let bundle_value = json!({
578578
"SignedEntryTimestamp": "MEUCIDBGJijj2FqU25yRWzlEWHqE64XKwUvychBs1bSM1PaKAiEAwcR2u81c42TLBk3lWJqhtB7SnM7Lh0OYEl6Bfa7ZA4s=",
579579
"Payload": {
@@ -583,7 +583,8 @@ OSWS1X9vPavpiQOoTTGC0xX57OojUadxF1cdQmrsiReWg2Wn4FneJfa8xw==
583583
"logID": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"
584584
}
585585
});
586-
let bundle: Bundle = serde_json::from_value(bundle_value).expect("Cannot parse bundle");
586+
let bundle: RekorBundle =
587+
serde_json::from_value(bundle_value).expect("Cannot parse bundle");
587588
bundle
588589
}
589590

@@ -887,7 +888,7 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ==
887888
let cert_pool = CertificatePool::from_certificates(&certs).unwrap();
888889

889890
let integrated_time = Utc::now().checked_sub_signed(Duration::minutes(1)).unwrap();
890-
let bundle = Bundle {
891+
let bundle = RekorBundle {
891892
signed_entry_timestamp: "not relevant".to_string(),
892893
payload: Payload {
893894
body: "not relevant".to_string(),
@@ -934,7 +935,7 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ==
934935
let cert_pool = CertificatePool::from_certificates(&certs).unwrap();
935936

936937
let integrated_time = Utc::now().checked_sub_signed(Duration::minutes(1)).unwrap();
937-
let bundle = Bundle {
938+
let bundle = RekorBundle {
938939
signed_entry_timestamp: "not relevant".to_string(),
939940
payload: Payload {
940941
body: "not relevant".to_string(),
@@ -980,7 +981,7 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ==
980981
let cert_pool = CertificatePool::from_certificates(&certs).unwrap();
981982

982983
let integrated_time = Utc::now().checked_sub_signed(Duration::minutes(1)).unwrap();
983-
let bundle = Bundle {
984+
let bundle = RekorBundle {
984985
signed_entry_timestamp: "not relevant".to_string(),
985986
payload: Payload {
986987
body: "not relevant".to_string(),

src/cosign/verification_constraint/certificate_verifier.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ impl VerificationConstraint for CertificateVerifier {
119119
#[cfg(test)]
120120
mod tests {
121121
use super::*;
122-
use crate::cosign::bundle::Bundle;
122+
use crate::cosign::bundle::RekorBundle;
123123
use crate::crypto::tests::*;
124124
use crate::registry;
125125

@@ -207,7 +207,7 @@ RAIgPixAn47x4qLpu7Y/d0oyvbnOGtD5cY7rywdMOO7LYRsCIDsCyGUZIYMFfSrt
207207
(signature_layer, cert_pem_raw)
208208
}
209209

210-
fn build_bundle() -> Bundle {
210+
fn build_bundle() -> RekorBundle {
211211
let bundle_value = json!({
212212
"SignedEntryTimestamp": "MEUCIG5TYOXkiPm7RGYgDIPHwRQW5NyoSPuwxvJe4ByB9c37AiEAyD0dVcsiJ5Lp+QY5SL80jDxfc75BtjRnticVf7SiFD0=",
213213
"Payload": {
@@ -217,7 +217,8 @@ RAIgPixAn47x4qLpu7Y/d0oyvbnOGtD5cY7rywdMOO7LYRsCIDsCyGUZIYMFfSrt
217217
"logID": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"
218218
}
219219
});
220-
let bundle: Bundle = serde_json::from_value(bundle_value).expect("Cannot parse bundle");
220+
let bundle: RekorBundle =
221+
serde_json::from_value(bundle_value).expect("Cannot parse bundle");
221222
bundle
222223
}
223224

0 commit comments

Comments
 (0)