1use sigstore_rekor::entry::LogEntry;
4use sigstore_types::{
5 bundle::{
6 CertificateContent, CheckpointData, InclusionPromise, InclusionProof, KindVersion, LogId,
7 MessageSignature, Rfc3161Timestamp, SignatureContent, TimestampVerificationData,
8 TransparencyLogEntry, VerificationMaterial, VerificationMaterialContent,
9 },
10 Bundle, CanonicalizedBody, DerCertificate, DsseEnvelope, LogIndex, LogKeyId, MediaType,
11 Sha256Hash, SignatureBytes, SignedTimestamp, TimestampToken,
12};
13
14#[derive(Debug, Clone)]
19pub enum VerificationMaterialV03 {
20 Certificate(DerCertificate),
22 PublicKey { hint: String },
24}
25
26#[derive(Debug, Clone)]
43pub struct BundleV03 {
44 pub verification: VerificationMaterialV03,
46 pub content: SignatureContent,
48 pub tlog_entries: Vec<TransparencyLogEntry>,
50 pub rfc3161_timestamps: Vec<Rfc3161Timestamp>,
52}
53
54impl BundleV03 {
55 pub fn new(verification: VerificationMaterialV03, content: SignatureContent) -> Self {
57 Self {
58 verification,
59 content,
60 tlog_entries: Vec::new(),
61 rfc3161_timestamps: Vec::new(),
62 }
63 }
64
65 pub fn with_certificate_and_signature(
69 certificate: DerCertificate,
70 signature: SignatureBytes,
71 artifact_digest: Sha256Hash,
72 ) -> Self {
73 Self::new(
74 VerificationMaterialV03::Certificate(certificate),
75 SignatureContent::MessageSignature(MessageSignature {
76 message_digest: Some(sigstore_types::bundle::MessageDigest {
77 algorithm: sigstore_types::HashAlgorithm::Sha2256,
78 digest: artifact_digest,
79 }),
80 signature,
81 }),
82 )
83 }
84
85 pub fn with_certificate_and_dsse(certificate: DerCertificate, envelope: DsseEnvelope) -> Self {
89 Self::new(
90 VerificationMaterialV03::Certificate(certificate),
91 SignatureContent::DsseEnvelope(envelope),
92 )
93 }
94
95 pub fn with_tlog_entry(mut self, entry: TransparencyLogEntry) -> Self {
97 self.tlog_entries.push(entry);
98 self
99 }
100
101 pub fn with_rfc3161_timestamp(mut self, timestamp: TimestampToken) -> Self {
103 self.rfc3161_timestamps.push(Rfc3161Timestamp {
104 signed_timestamp: timestamp,
105 });
106 self
107 }
108
109 pub fn into_bundle(self) -> Bundle {
111 let verification_content = match self.verification {
112 VerificationMaterialV03::Certificate(cert) => {
113 VerificationMaterialContent::Certificate(CertificateContent { raw_bytes: cert })
114 }
115 VerificationMaterialV03::PublicKey { hint } => {
116 VerificationMaterialContent::PublicKey { hint }
117 }
118 };
119
120 Bundle {
121 media_type: MediaType::Bundle0_3.as_str().to_string(),
122 verification_material: VerificationMaterial {
123 content: verification_content,
124 tlog_entries: self.tlog_entries,
125 timestamp_verification_data: TimestampVerificationData {
126 rfc3161_timestamps: self.rfc3161_timestamps,
127 },
128 },
129 content: self.content,
130 }
131 }
132}
133
134pub struct TlogEntryBuilder {
136 log_index: i64,
137 log_id: String,
138 kind: String,
139 kind_version: String,
140 integrated_time: i64,
141 canonicalized_body: Vec<u8>,
142 inclusion_promise: Option<InclusionPromise>,
143 inclusion_proof: Option<InclusionProof>,
144}
145
146impl TlogEntryBuilder {
147 pub fn new() -> Self {
149 Self {
150 log_index: 0,
151 log_id: String::new(),
152 kind: "hashedrekord".to_string(),
153 kind_version: "0.0.1".to_string(),
154 integrated_time: 0,
155 canonicalized_body: Vec::new(),
156 inclusion_promise: None,
157 inclusion_proof: None,
158 }
159 }
160
161 pub fn from_log_entry(entry: &LogEntry, kind: &str, version: &str) -> Self {
171 let log_id_base64 = entry
173 .log_id
174 .to_base64()
175 .unwrap_or_else(|_| entry.log_id.to_string());
176
177 let mut builder = Self {
178 log_index: entry.log_index,
179 log_id: log_id_base64,
180 kind: kind.to_string(),
181 kind_version: version.to_string(),
182 integrated_time: entry.integrated_time,
183 canonicalized_body: entry.body.as_bytes().to_vec(),
184 inclusion_promise: None,
185 inclusion_proof: None,
186 };
187
188 if let Some(verification) = &entry.verification {
190 if let Some(set) = &verification.signed_entry_timestamp {
191 builder.inclusion_promise = Some(InclusionPromise {
192 signed_entry_timestamp: set.clone(),
193 });
194 }
195
196 if let Some(proof) = &verification.inclusion_proof {
197 let root_hash = Sha256Hash::from_hex(&proof.root_hash)
200 .unwrap_or_else(|_| Sha256Hash::from_bytes([0u8; 32]));
201
202 let hashes: Vec<Sha256Hash> = proof
204 .hashes
205 .iter()
206 .filter_map(|h| Sha256Hash::from_hex(h).ok())
207 .collect();
208
209 builder.inclusion_proof = Some(InclusionProof {
210 log_index: LogIndex::new(proof.log_index),
211 root_hash,
212 tree_size: proof.tree_size,
213 hashes,
214 checkpoint: CheckpointData {
215 envelope: proof.checkpoint.clone(),
216 },
217 });
218 }
219 }
220
221 builder
222 }
223
224 pub fn log_index(mut self, index: i64) -> Self {
226 self.log_index = index;
227 self
228 }
229
230 pub fn integrated_time(mut self, time: i64) -> Self {
232 self.integrated_time = time;
233 self
234 }
235
236 pub fn inclusion_promise(mut self, signed_entry_timestamp: SignedTimestamp) -> Self {
238 self.inclusion_promise = Some(InclusionPromise {
239 signed_entry_timestamp,
240 });
241 self
242 }
243
244 pub fn inclusion_proof(
253 mut self,
254 log_index: i64,
255 root_hash: Sha256Hash,
256 tree_size: i64,
257 hashes: Vec<Sha256Hash>,
258 checkpoint: String,
259 ) -> Self {
260 self.inclusion_proof = Some(InclusionProof {
261 log_index: LogIndex::from(log_index),
262 root_hash,
263 tree_size,
264 hashes,
265 checkpoint: CheckpointData {
266 envelope: checkpoint,
267 },
268 });
269 self
270 }
271
272 pub fn build(self) -> TransparencyLogEntry {
274 TransparencyLogEntry {
275 log_index: LogIndex::from(self.log_index),
276 log_id: LogId {
277 key_id: LogKeyId::new(self.log_id),
278 },
279 kind_version: KindVersion {
280 kind: self.kind,
281 version: self.kind_version,
282 },
283 integrated_time: self.integrated_time,
284 inclusion_promise: self.inclusion_promise,
285 inclusion_proof: self.inclusion_proof,
286 canonicalized_body: CanonicalizedBody::new(self.canonicalized_body),
287 }
288 }
289}
290
291impl Default for TlogEntryBuilder {
292 fn default() -> Self {
293 Self::new()
294 }
295}