|
51 | 51 | from cryptography.hazmat.primitives.asymmetric import ec |
52 | 52 | from cryptography.hazmat.primitives.asymmetric.utils import Prehashed |
53 | 53 | from cryptography.x509.oid import NameOID |
54 | | -from pydantic import BaseModel |
55 | 54 | from sigstore_protobuf_specs.dev.sigstore.bundle.v1 import ( |
56 | 55 | Bundle, |
57 | 56 | VerificationMaterial, |
@@ -175,7 +174,7 @@ def _signing_cert( |
175 | 174 | def sign( |
176 | 175 | self, |
177 | 176 | input_: IO[bytes], |
178 | | - ) -> SigningResult: |
| 177 | + ) -> Bundle: |
179 | 178 | """Public API for signing blobs""" |
180 | 179 | input_digest = sha256_streaming(input_) |
181 | 180 | private_key = self._private_key |
@@ -233,7 +232,7 @@ def sign( |
233 | 232 |
|
234 | 233 | logger.debug(f"Transparency log entry created with index: {entry.log_index}") |
235 | 234 |
|
236 | | - return SigningResult( |
| 235 | + return _make_bundle( |
237 | 236 | input_digest=HexStr(input_digest.hex()), |
238 | 237 | cert_pem=PEMCert( |
239 | 238 | cert.public_bytes(encoding=serialization.Encoding.PEM).decode() |
@@ -308,90 +307,60 @@ def signer( |
308 | 307 | yield Signer(identity_token, self, cache) |
309 | 308 |
|
310 | 309 |
|
311 | | -class SigningResult(BaseModel): |
| 310 | +def _make_bundle( |
| 311 | + input_digest: HexStr, cert_pem: PEMCert, b64_signature: B64Str, log_entry: LogEntry |
| 312 | +) -> Bundle: |
312 | 313 | """ |
313 | | - Represents the artifacts of a signing operation. |
| 314 | + Convert the raw results of a Sigstore signing operation into a Sigstore bundle. |
314 | 315 | """ |
315 | 316 |
|
316 | | - input_digest: HexStr |
317 | | - """ |
318 | | - The hex-encoded SHA256 digest of the input that was signed for. |
319 | | - """ |
320 | | - |
321 | | - cert_pem: PEMCert |
322 | | - """ |
323 | | - The PEM-encoded public half of the certificate used for signing. |
324 | | - """ |
325 | | - |
326 | | - b64_signature: B64Str |
327 | | - """ |
328 | | - The base64-encoded signature. |
329 | | - """ |
330 | | - |
331 | | - log_entry: LogEntry |
332 | | - """ |
333 | | - A record of the Rekor log entry for the signing operation. |
334 | | - """ |
335 | | - |
336 | | - def to_bundle(self) -> Bundle: |
337 | | - """ |
338 | | - Creates a Sigstore bundle (as defined by Sigstore's protobuf specs) |
339 | | - from this `SigningResult`. |
340 | | - """ |
341 | | - |
342 | | - # NOTE: We explicitly only include the leaf certificate in the bundle's "chain" |
343 | | - # here: the specs explicitly forbid the inclusion of the root certificate, |
344 | | - # and discourage inclusion of any intermediates (since they're in the root of |
345 | | - # trust already). |
346 | | - cert = x509.load_pem_x509_certificate(self.cert_pem.encode()) |
347 | | - cert_der = cert.public_bytes(encoding=serialization.Encoding.DER) |
348 | | - chain = X509CertificateChain(certificates=[X509Certificate(raw_bytes=cert_der)]) |
349 | | - |
350 | | - inclusion_proof: InclusionProof | None = None |
351 | | - if self.log_entry.inclusion_proof is not None: |
352 | | - inclusion_proof = InclusionProof( |
353 | | - log_index=self.log_entry.inclusion_proof.log_index, |
354 | | - root_hash=bytes.fromhex(self.log_entry.inclusion_proof.root_hash), |
355 | | - tree_size=self.log_entry.inclusion_proof.tree_size, |
356 | | - hashes=[ |
357 | | - bytes.fromhex(h) for h in self.log_entry.inclusion_proof.hashes |
358 | | - ], |
359 | | - checkpoint=Checkpoint( |
360 | | - envelope=self.log_entry.inclusion_proof.checkpoint |
361 | | - ), |
362 | | - ) |
363 | | - |
364 | | - tlog_entry = TransparencyLogEntry( |
365 | | - log_index=self.log_entry.log_index, |
366 | | - log_id=LogId(key_id=bytes.fromhex(self.log_entry.log_id)), |
367 | | - kind_version=KindVersion(kind="hashedrekord", version="0.0.1"), |
368 | | - integrated_time=self.log_entry.integrated_time, |
369 | | - inclusion_promise=InclusionPromise( |
370 | | - signed_entry_timestamp=base64.b64decode( |
371 | | - self.log_entry.inclusion_promise |
372 | | - ) |
373 | | - ) |
374 | | - if self.log_entry.inclusion_promise |
375 | | - else None, |
376 | | - inclusion_proof=inclusion_proof, |
377 | | - canonicalized_body=base64.b64decode(self.log_entry.body), |
| 317 | + # NOTE: We explicitly only include the leaf certificate in the bundle's "chain" |
| 318 | + # here: the specs explicitly forbid the inclusion of the root certificate, |
| 319 | + # and discourage inclusion of any intermediates (since they're in the root of |
| 320 | + # trust already). |
| 321 | + cert = x509.load_pem_x509_certificate(cert_pem.encode()) |
| 322 | + cert_der = cert.public_bytes(encoding=serialization.Encoding.DER) |
| 323 | + chain = X509CertificateChain(certificates=[X509Certificate(raw_bytes=cert_der)]) |
| 324 | + |
| 325 | + inclusion_proof: InclusionProof | None = None |
| 326 | + if log_entry.inclusion_proof is not None: |
| 327 | + inclusion_proof = InclusionProof( |
| 328 | + log_index=log_entry.inclusion_proof.log_index, |
| 329 | + root_hash=bytes.fromhex(log_entry.inclusion_proof.root_hash), |
| 330 | + tree_size=log_entry.inclusion_proof.tree_size, |
| 331 | + hashes=[bytes.fromhex(h) for h in log_entry.inclusion_proof.hashes], |
| 332 | + checkpoint=Checkpoint(envelope=log_entry.inclusion_proof.checkpoint), |
378 | 333 | ) |
379 | 334 |
|
380 | | - material = VerificationMaterial( |
381 | | - x509_certificate_chain=chain, |
382 | | - tlog_entries=[tlog_entry], |
| 335 | + tlog_entry = TransparencyLogEntry( |
| 336 | + log_index=log_entry.log_index, |
| 337 | + log_id=LogId(key_id=bytes.fromhex(log_entry.log_id)), |
| 338 | + kind_version=KindVersion(kind="hashedrekord", version="0.0.1"), |
| 339 | + integrated_time=log_entry.integrated_time, |
| 340 | + inclusion_promise=InclusionPromise( |
| 341 | + signed_entry_timestamp=base64.b64decode(log_entry.inclusion_promise) |
383 | 342 | ) |
384 | | - |
385 | | - bundle = Bundle( |
386 | | - media_type="application/vnd.dev.sigstore.bundle+json;version=0.2", |
387 | | - verification_material=material, |
388 | | - message_signature=MessageSignature( |
389 | | - message_digest=HashOutput( |
390 | | - algorithm=HashAlgorithm.SHA2_256, |
391 | | - digest=bytes.fromhex(self.input_digest), |
392 | | - ), |
393 | | - signature=base64.b64decode(self.b64_signature), |
| 343 | + if log_entry.inclusion_promise |
| 344 | + else None, |
| 345 | + inclusion_proof=inclusion_proof, |
| 346 | + canonicalized_body=base64.b64decode(log_entry.body), |
| 347 | + ) |
| 348 | + |
| 349 | + material = VerificationMaterial( |
| 350 | + x509_certificate_chain=chain, |
| 351 | + tlog_entries=[tlog_entry], |
| 352 | + ) |
| 353 | + |
| 354 | + bundle = Bundle( |
| 355 | + media_type="application/vnd.dev.sigstore.bundle+json;version=0.2", |
| 356 | + verification_material=material, |
| 357 | + message_signature=MessageSignature( |
| 358 | + message_digest=HashOutput( |
| 359 | + algorithm=HashAlgorithm.SHA2_256, |
| 360 | + digest=bytes.fromhex(input_digest), |
394 | 361 | ), |
395 | | - ) |
| 362 | + signature=base64.b64decode(b64_signature), |
| 363 | + ), |
| 364 | + ) |
396 | 365 |
|
397 | | - return bundle |
| 366 | + return bundle |
0 commit comments