Skip to content

Commit e5c4c49

Browse files
committed
simplifications by only supporting v03 bundle creation
1 parent d355c3b commit e5c4c49

File tree

5 files changed

+146
-287
lines changed

5 files changed

+146
-287
lines changed

crates/sigstore-bundle/README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This crate handles Sigstore bundle creation, parsing, and validation. A Sigstore
99
## Features
1010

1111
- **Bundle parsing**: Load bundles from JSON (v0.1, v0.2, v0.3 formats)
12-
- **Bundle creation**: Build bundles programmatically with `BundleBuilder`
12+
- **Bundle creation**: Build v0.3 bundles with type-safe `BundleV03`
1313
- **Validation**: Structural validation of bundle contents
1414
- **Version handling**: Support for multiple bundle format versions
1515
- **Media type detection**: Automatic format detection from media type
@@ -25,7 +25,7 @@ This crate handles Sigstore bundle creation, parsing, and validation. A Sigstore
2525
## Usage
2626

2727
```rust
28-
use sigstore_bundle::{BundleBuilder, ValidationOptions};
28+
use sigstore_bundle::{BundleV03, ValidationOptions};
2929
use sigstore_types::Bundle;
3030

3131
// Parse a bundle
@@ -35,12 +35,10 @@ let bundle: Bundle = serde_json::from_str(bundle_json)?;
3535
let options = ValidationOptions::default();
3636
sigstore_bundle::validate(&bundle, &options)?;
3737

38-
// Build a bundle
39-
let bundle = BundleBuilder::new()
40-
.certificate_chain(certs)
41-
.signature(signature)
42-
.tlog_entry(entry)
43-
.build()?;
38+
// Build a v0.3 bundle (type-safe: certificate chains not allowed)
39+
let bundle = BundleV03::with_certificate_and_signature(cert_der, signature, artifact_hash)
40+
.with_tlog_entry(entry)
41+
.into_bundle();
4442
```
4543

4644
## Related Crates

crates/sigstore-bundle/src/builder.rs

Lines changed: 96 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -4,138 +4,135 @@ use sigstore_crypto::Signature;
44
use sigstore_rekor::entry::LogEntry;
55
use sigstore_types::{
66
bundle::{
7-
CheckpointData, InclusionPromise, InclusionProof, KindVersion, LogId, MessageSignature,
8-
Rfc3161Timestamp, SignatureContent, TimestampVerificationData, TransparencyLogEntry,
9-
VerificationMaterial, VerificationMaterialContent,
7+
CertificateContent, CheckpointData, InclusionPromise, InclusionProof, KindVersion, LogId,
8+
MessageSignature, Rfc3161Timestamp, SignatureContent, TimestampVerificationData,
9+
TransparencyLogEntry, VerificationMaterial, VerificationMaterialContent,
1010
},
1111
Bundle, CanonicalizedBody, DerCertificate, DsseEnvelope, LogKeyId, MediaType, Sha256Hash,
1212
SignatureBytes, SignedTimestamp, TimestampToken,
1313
};
1414

15-
/// Builder for creating Sigstore bundles
16-
pub struct BundleBuilder {
17-
/// Bundle version
18-
version: MediaType,
19-
/// Verification material content
20-
verification_content: Option<VerificationMaterialContent>,
15+
/// Verification material for v0.3 bundles.
16+
///
17+
/// In v0.3 bundles, only a single certificate or a public key hint is allowed.
18+
/// Certificate chains are NOT permitted in v0.3 format.
19+
#[derive(Debug, Clone)]
20+
pub enum VerificationMaterialV03 {
21+
/// Single certificate (the common case for Fulcio-issued certs)
22+
Certificate(DerCertificate),
23+
/// Public key hint (for pre-existing keys)
24+
PublicKey { hint: String },
25+
}
26+
27+
/// A Sigstore bundle in v0.3 format.
28+
///
29+
/// The v0.3 format requires:
30+
/// - A single certificate (not a chain) or public key hint
31+
/// - Either a message signature or DSSE envelope
32+
/// - Optional transparency log entries and RFC 3161 timestamps
33+
///
34+
/// # Example
35+
///
36+
/// ```ignore
37+
/// use sigstore_bundle::BundleV03;
38+
///
39+
/// let bundle = BundleV03::with_certificate_and_signature(cert_der, signature, artifact_hash)
40+
/// .with_tlog_entry(tlog_entry)
41+
/// .into_bundle();
42+
/// ```
43+
#[derive(Debug, Clone)]
44+
pub struct BundleV03 {
45+
/// Verification material - either a certificate or public key
46+
pub verification: VerificationMaterialV03,
47+
/// The signature content (message signature or DSSE envelope)
48+
pub content: SignatureContent,
2149
/// Transparency log entries
22-
tlog_entries: Vec<TransparencyLogEntry>,
50+
pub tlog_entries: Vec<TransparencyLogEntry>,
2351
/// RFC 3161 timestamps
24-
rfc3161_timestamps: Vec<Rfc3161Timestamp>,
25-
/// Signature content
26-
signature_content: Option<SignatureContent>,
52+
pub rfc3161_timestamps: Vec<Rfc3161Timestamp>,
2753
}
2854

29-
impl BundleBuilder {
30-
/// Create a new bundle builder with default version (0.3)
31-
pub fn new() -> Self {
55+
impl BundleV03 {
56+
/// Create a new v0.3 bundle with the required fields.
57+
pub fn new(verification: VerificationMaterialV03, content: SignatureContent) -> Self {
3258
Self {
33-
version: MediaType::Bundle0_3,
34-
verification_content: None,
59+
verification,
60+
content,
3561
tlog_entries: Vec::new(),
3662
rfc3161_timestamps: Vec::new(),
37-
signature_content: None,
3863
}
3964
}
4065

41-
/// Set the bundle version
42-
pub fn version(mut self, version: MediaType) -> Self {
43-
self.version = version;
44-
self
45-
}
46-
47-
/// Set the signing certificate from DER bytes
48-
pub fn certificate(mut self, cert_der: Vec<u8>) -> Self {
49-
self.verification_content = Some(VerificationMaterialContent::Certificate(
50-
sigstore_types::bundle::CertificateContent {
51-
raw_bytes: DerCertificate::new(cert_der),
52-
},
53-
));
54-
self
55-
}
56-
57-
/// Set the certificate chain from DER bytes
58-
pub fn certificate_chain(mut self, certs_der: Vec<Vec<u8>>) -> Self {
59-
self.verification_content = Some(VerificationMaterialContent::X509CertificateChain {
60-
certificates: certs_der
61-
.into_iter()
62-
.map(|c| sigstore_types::bundle::X509Certificate {
63-
raw_bytes: DerCertificate::new(c),
64-
})
65-
.collect(),
66-
});
67-
self
66+
/// Create a new v0.3 bundle with a certificate and message signature.
67+
///
68+
/// This is the most common case for Sigstore signing with Fulcio certificates.
69+
pub fn with_certificate_and_signature(
70+
cert_der: Vec<u8>,
71+
signature: Signature,
72+
artifact_digest: Sha256Hash,
73+
) -> Self {
74+
Self::new(
75+
VerificationMaterialV03::Certificate(DerCertificate::new(cert_der)),
76+
SignatureContent::MessageSignature(MessageSignature {
77+
message_digest: Some(sigstore_types::bundle::MessageDigest {
78+
algorithm: sigstore_types::HashAlgorithm::Sha2256,
79+
digest: artifact_digest,
80+
}),
81+
signature: SignatureBytes::new(signature.into_bytes()),
82+
}),
83+
)
6884
}
6985

70-
/// Set the public key hint
71-
pub fn public_key(mut self, hint: String) -> Self {
72-
self.verification_content = Some(VerificationMaterialContent::PublicKey { hint });
73-
self
86+
/// Create a new v0.3 bundle with a certificate and DSSE envelope.
87+
///
88+
/// Used for attestations (in-toto statements).
89+
pub fn with_certificate_and_dsse(cert_der: Vec<u8>, envelope: DsseEnvelope) -> Self {
90+
Self::new(
91+
VerificationMaterialV03::Certificate(DerCertificate::new(cert_der)),
92+
SignatureContent::DsseEnvelope(envelope),
93+
)
7494
}
7595

76-
/// Add a transparency log entry
77-
pub fn add_tlog_entry(mut self, entry: TransparencyLogEntry) -> Self {
96+
/// Add a transparency log entry.
97+
pub fn with_tlog_entry(mut self, entry: TransparencyLogEntry) -> Self {
7898
self.tlog_entries.push(entry);
7999
self
80100
}
81101

82-
/// Add an RFC 3161 timestamp from DER bytes
83-
pub fn add_rfc3161_timestamp(mut self, signed_timestamp: Vec<u8>) -> Self {
102+
/// Add an RFC 3161 timestamp.
103+
pub fn with_rfc3161_timestamp(mut self, signed_timestamp: Vec<u8>) -> Self {
84104
self.rfc3161_timestamps.push(Rfc3161Timestamp {
85105
signed_timestamp: TimestampToken::new(signed_timestamp),
86106
});
87107
self
88108
}
89109

90-
/// Set the message signature with artifact digest
91-
///
92-
/// The digest is required for compatibility with cosign and other Sigstore tools.
93-
pub fn message_signature(mut self, signature: Signature, artifact_digest: Sha256Hash) -> Self {
94-
self.signature_content = Some(SignatureContent::MessageSignature(MessageSignature {
95-
message_digest: Some(sigstore_types::bundle::MessageDigest {
96-
algorithm: sigstore_types::HashAlgorithm::Sha2256,
97-
digest: artifact_digest,
98-
}),
99-
signature: SignatureBytes::new(signature.into_bytes()),
100-
}));
101-
self
102-
}
103-
104-
/// Set the DSSE envelope
105-
pub fn dsse_envelope(mut self, envelope: DsseEnvelope) -> Self {
106-
self.signature_content = Some(SignatureContent::DsseEnvelope(envelope));
107-
self
108-
}
109-
110-
/// Build the bundle
111-
pub fn build(self) -> Result<Bundle, &'static str> {
112-
let verification_content = self
113-
.verification_content
114-
.ok_or("verification material not set")?;
115-
116-
let signature_content = self.signature_content.ok_or("signature content not set")?;
110+
/// Convert to a serializable Bundle.
111+
pub fn into_bundle(self) -> Bundle {
112+
let verification_content = match self.verification {
113+
VerificationMaterialV03::Certificate(cert) => {
114+
VerificationMaterialContent::Certificate(CertificateContent { raw_bytes: cert })
115+
}
116+
VerificationMaterialV03::PublicKey { hint } => {
117+
VerificationMaterialContent::PublicKey { hint }
118+
}
119+
};
117120

118-
Ok(Bundle {
119-
media_type: self.version.as_str().to_string(),
121+
Bundle {
122+
media_type: MediaType::Bundle0_3.as_str().to_string(),
120123
verification_material: VerificationMaterial {
121124
content: verification_content,
122125
tlog_entries: self.tlog_entries,
123126
timestamp_verification_data: TimestampVerificationData {
124127
rfc3161_timestamps: self.rfc3161_timestamps,
125128
},
126129
},
127-
content: signature_content,
128-
})
129-
}
130-
}
131-
132-
impl Default for BundleBuilder {
133-
fn default() -> Self {
134-
Self::new()
130+
content: self.content,
131+
}
135132
}
136133
}
137134

138-
/// Helper to create a transparency log entry
135+
/// Helper to create a transparency log entry.
139136
pub struct TlogEntryBuilder {
140137
log_index: u64,
141138
log_id: String,
@@ -148,7 +145,7 @@ pub struct TlogEntryBuilder {
148145
}
149146

150147
impl TlogEntryBuilder {
151-
/// Create a new tlog entry builder
148+
/// Create a new tlog entry builder.
152149
pub fn new() -> Self {
153150
Self {
154151
log_index: 0,
@@ -162,7 +159,7 @@ impl TlogEntryBuilder {
162159
}
163160
}
164161

165-
/// Create a tlog entry builder from a Rekor LogEntry response
162+
/// Create a tlog entry builder from a Rekor LogEntry response.
166163
///
167164
/// This method extracts all relevant fields from a Rekor API response
168165
/// and populates the builder automatically.
@@ -225,27 +222,27 @@ impl TlogEntryBuilder {
225222
builder
226223
}
227224

228-
/// Set the log index
225+
/// Set the log index.
229226
pub fn log_index(mut self, index: u64) -> Self {
230227
self.log_index = index;
231228
self
232229
}
233230

234-
/// Set the integrated time (Unix timestamp)
231+
/// Set the integrated time (Unix timestamp).
235232
pub fn integrated_time(mut self, time: u64) -> Self {
236233
self.integrated_time = time;
237234
self
238235
}
239236

240-
/// Set the inclusion promise (Signed Entry Timestamp)
237+
/// Set the inclusion promise (Signed Entry Timestamp).
241238
pub fn inclusion_promise(mut self, signed_entry_timestamp: SignedTimestamp) -> Self {
242239
self.inclusion_promise = Some(InclusionPromise {
243240
signed_entry_timestamp,
244241
});
245242
self
246243
}
247244

248-
/// Set the inclusion proof
245+
/// Set the inclusion proof.
249246
///
250247
/// # Arguments
251248
/// * `log_index` - The log index
@@ -273,7 +270,7 @@ impl TlogEntryBuilder {
273270
self
274271
}
275272

276-
/// Build the transparency log entry
273+
/// Build the transparency log entry.
277274
pub fn build(self) -> TransparencyLogEntry {
278275
TransparencyLogEntry {
279276
log_index: self.log_index.to_string().into(),

crates/sigstore-bundle/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ pub mod builder;
77
pub mod error;
88
pub mod validation;
99

10-
pub use builder::{BundleBuilder, TlogEntryBuilder};
10+
pub use builder::{BundleV03, TlogEntryBuilder, VerificationMaterialV03};
1111
pub use error::{Error, Result};
1212
pub use validation::{validate_bundle, validate_bundle_with_options, ValidationOptions};

0 commit comments

Comments
 (0)