5151from cryptography .hazmat .primitives .asymmetric import ec
5252from cryptography .hazmat .primitives .asymmetric .utils import Prehashed
5353from cryptography .x509 .oid import NameOID
54+ from in_toto_attestation .v1 .statement import Statement
5455from sigstore_protobuf_specs .dev .sigstore .bundle .v1 import (
5556 Bundle ,
5657 VerificationMaterial ,
7071 KindVersion ,
7172 TransparencyLogEntry ,
7273)
74+ from sigstore_protobuf_specs .io .intoto import Envelope
7375
76+ from sigstore ._internal import dsse
7477from sigstore ._internal .fulcio import (
7578 ExpiredCertificate ,
7679 FulcioCertificateSigningResponse ,
7982from sigstore ._internal .rekor .client import RekorClient
8083from sigstore ._internal .sct import verify_sct
8184from sigstore ._internal .trustroot import TrustedRoot
82- from sigstore ._utils import B64Str , HexStr , PEMCert , sha256_streaming
85+ from sigstore ._utils import PEMCert , sha256_streaming
8386from sigstore .oidc import ExpiredIdentity , IdentityToken
8487from sigstore .transparency import LogEntry
8588
@@ -173,10 +176,9 @@ def _signing_cert(
173176
174177 def sign (
175178 self ,
176- input_ : IO [bytes ],
179+ input_ : IO [bytes ] | Statement ,
177180 ) -> Bundle :
178181 """Public API for signing blobs"""
179- input_digest = sha256_streaming (input_ )
180182 private_key = self ._private_key
181183
182184 if not self ._identity_token .in_validity_period ():
@@ -187,57 +189,78 @@ def sign(
187189 except ExpiredCertificate as e :
188190 raise e
189191
190- # TODO(alex): Retrieve the public key via TUF
191- #
192192 # Verify the SCT
193- sct = certificate_response .sct # noqa
194- cert = certificate_response .cert # noqa
193+ sct = certificate_response .sct
194+ cert = certificate_response .cert
195195 chain = certificate_response .chain
196196
197197 verify_sct (sct , cert , chain , self ._signing_ctx ._rekor ._ct_keyring )
198198
199199 logger .debug ("Successfully verified SCT..." )
200200
201- # Sign artifact
202- artifact_signature = private_key .sign (
203- input_digest , ec .ECDSA (Prehashed (hashes .SHA256 ()))
204- )
205- b64_artifact_signature = B64Str (base64 .b64encode (artifact_signature ).decode ())
206-
207201 # Prepare inputs
208202 b64_cert = base64 .b64encode (
209203 cert .public_bytes (encoding = serialization .Encoding .PEM )
210204 )
211205
212- # Create the transparency log entry
213- proposed_entry = rekor_types .Hashedrekord (
214- kind = "hashedrekord" ,
215- api_version = "0.0.1" ,
216- spec = rekor_types .hashedrekord .HashedrekordV001Schema (
217- signature = rekor_types .hashedrekord .Signature (
218- content = b64_artifact_signature ,
219- public_key = rekor_types .hashedrekord .PublicKey (
220- content = b64_cert .decode ()
206+ # Sign artifact
207+ content : MessageSignature | Envelope
208+ proposed_entry : rekor_types .Hashedrekord | rekor_types .Dsse
209+ if isinstance (input_ , Statement ):
210+ content = dsse .sign_intoto (private_key , input_ )
211+
212+ # Create the proposed DSSE entry
213+ proposed_entry = rekor_types .Dsse (
214+ spec = rekor_types .dsse .DsseV001Schema (
215+ proposed_content = rekor_types .dsse .ProposedContent (
216+ envelope = content .to_json (),
217+ verifiers = [b64_cert .decode ()],
221218 ),
222219 ),
223- data = rekor_types .hashedrekord .Data (
224- hash = rekor_types .hashedrekord .Hash (
225- algorithm = rekor_types .hashedrekord .Algorithm .SHA256 ,
226- value = input_digest .hex (),
227- )
220+ )
221+ else :
222+ input_digest = sha256_streaming (input_ )
223+
224+ artifact_signature = private_key .sign (
225+ input_digest , ec .ECDSA (Prehashed (hashes .SHA256 ()))
226+ )
227+
228+ content = MessageSignature (
229+ message_digest = HashOutput (
230+ algorithm = HashAlgorithm .SHA2_256 ,
231+ digest = input_digest ,
228232 ),
229- ),
230- )
233+ signature = artifact_signature ,
234+ )
235+
236+ # Create the proposed hashedrekord entry
237+ proposed_entry = rekor_types .Hashedrekord (
238+ spec = rekor_types .hashedrekord .HashedrekordV001Schema (
239+ signature = rekor_types .hashedrekord .Signature (
240+ content = base64 .b64encode (artifact_signature ).decode (),
241+ public_key = rekor_types .hashedrekord .PublicKey (
242+ content = b64_cert .decode ()
243+ ),
244+ ),
245+ data = rekor_types .hashedrekord .Data (
246+ hash = rekor_types .hashedrekord .Hash (
247+ algorithm = rekor_types .hashedrekord .Algorithm .SHA256 ,
248+ value = input_digest .hex (),
249+ )
250+ ),
251+ ),
252+ )
253+
254+ # Submit the proposed entry to the transparency log
231255 entry = self ._signing_ctx ._rekor .log .entries .post (proposed_entry )
232256
233257 logger .debug (f"Transparency log entry created with index: { entry .log_index } " )
234258
235259 return _make_bundle (
236- input_digest = HexStr ( input_digest . hex ()) ,
260+ content = content ,
237261 cert_pem = PEMCert (
238262 cert .public_bytes (encoding = serialization .Encoding .PEM ).decode ()
239263 ),
240- b64_signature = B64Str (b64_artifact_signature ),
241264 log_entry = entry ,
242265 )
243266
@@ -308,7 +331,9 @@ def signer(
308331
309332
310333def _make_bundle (
311- input_digest : HexStr , cert_pem : PEMCert , b64_signature : B64Str , log_entry : LogEntry
334+ content : MessageSignature | Envelope ,
335+ cert_pem : PEMCert ,
336+ log_entry : LogEntry ,
312337) -> Bundle :
313338 """
314339 Convert the raw results of a Sigstore signing operation into a Sigstore bundle.
@@ -332,10 +357,16 @@ def _make_bundle(
332357 checkpoint = Checkpoint (envelope = log_entry .inclusion_proof .checkpoint ),
333358 )
334359
360+ # TODO: This is a bit of a hack.
361+ if isinstance (content , MessageSignature ):
362+ kind_version = KindVersion (kind = "hashedrekord" , version = "0.0.1" )
363+ else :
364+ kind_version = KindVersion (kind = "dsse" , version = "0.0.1" )
365+
335366 tlog_entry = TransparencyLogEntry (
336367 log_index = log_entry .log_index ,
337368 log_id = LogId (key_id = bytes .fromhex (log_entry .log_id )),
338- kind_version = KindVersion ( kind = "hashedrekord" , version = "0.0.1" ) ,
369+ kind_version = kind_version ,
339370 integrated_time = log_entry .integrated_time ,
340371 inclusion_promise = InclusionPromise (
341372 signed_entry_timestamp = base64 .b64decode (log_entry .inclusion_promise )
@@ -354,13 +385,11 @@ def _make_bundle(
354385 bundle = Bundle (
355386 media_type = "application/vnd.dev.sigstore.bundle+json;version=0.2" ,
356387 verification_material = material ,
357- message_signature = MessageSignature (
358- message_digest = HashOutput (
359- algorithm = HashAlgorithm .SHA2_256 ,
360- digest = bytes .fromhex (input_digest ),
361- ),
362- signature = base64 .b64decode (b64_signature ),
363- ),
364388 )
365389
390+ if isinstance (content , MessageSignature ):
391+ bundle .message_signature = content
392+ else :
393+ bundle .dsse_envelope = content
394+
366395 return bundle
0 commit comments