@@ -4,138 +4,135 @@ use sigstore_crypto::Signature;
44use sigstore_rekor:: entry:: LogEntry ;
55use 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.
139136pub struct TlogEntryBuilder {
140137 log_index : u64 ,
141138 log_id : String ,
@@ -148,7 +145,7 @@ pub struct TlogEntryBuilder {
148145}
149146
150147impl 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 ( ) ,
0 commit comments