Skip to content

Commit d4f3bcf

Browse files
committed
port rekor v2 models, get tests passing locally
1 parent 876cd01 commit d4f3bcf

File tree

11 files changed

+83
-383
lines changed

11 files changed

+83
-383
lines changed

sigstore/_internal/rekor/client_v2.py

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@
1818

1919
from __future__ import annotations
2020

21+
import base64
2122
import json
2223
import logging
2324

2425
import requests
2526
from cryptography.hazmat.primitives import serialization
2627
from cryptography.x509 import Certificate
28+
from sigstore_models import intoto
29+
from sigstore_models.common import v1 as common_v1
30+
from sigstore_models.rekor import v2 as rekor_v2
2731
from sigstore_models.rekor.v1 import TransparencyLogEntry as _TransparencyLogEntry
28-
from sigstore_protobuf_specs.dev.sigstore.common import v1 as common_v1
29-
from sigstore_protobuf_specs.dev.sigstore.rekor import v2
30-
from sigstore_protobuf_specs.io import intoto
3132

3233
from sigstore._internal import USER_AGENT
3334
from sigstore._internal.key_details import _get_key_details
@@ -104,15 +105,17 @@ def _build_hashed_rekord_request(
104105
"""
105106
Construct a hashed rekord request to submit to Rekor.
106107
"""
107-
req = v2.CreateEntryRequest(
108-
hashed_rekord_request_v002=v2.HashedRekordRequestV002(
108+
req = rekor_v2.entry.CreateEntryRequest(
109+
hashed_rekord_request_v002=rekor_v2.hashedrekord.HashedRekordRequestV002(
109110
digest=hashed_input.digest,
110-
signature=v2.Signature(
111+
signature=rekor_v2.verifier.Signature(
111112
content=signature,
112-
verifier=v2.Verifier(
113+
verifier=rekor_v2.verifier.Verifier(
113114
x509_certificate=common_v1.X509Certificate(
114-
raw_bytes=certificate.public_bytes(
115-
encoding=serialization.Encoding.DER
115+
raw_bytes=base64.b64encode(
116+
certificate.public_bytes(
117+
encoding=serialization.Encoding.DER
118+
)
116119
)
117120
),
118121
key_details=_get_key_details(certificate),
@@ -129,24 +132,16 @@ def _build_dsse_request(
129132
"""
130133
Construct a dsse request to submit to Rekor.
131134
"""
132-
req = v2.CreateEntryRequest(
133-
dsse_request_v002=v2.DsseRequestV002(
134-
envelope=intoto.Envelope(
135-
payload=envelope._inner.payload,
136-
payload_type=envelope._inner.payload_type,
137-
signatures=[
138-
intoto.Signature(
139-
keyid=signature.keyid, # type: ignore[arg-type]
140-
sig=signature.sig,
141-
)
142-
for signature in envelope._inner.signatures
143-
],
144-
),
135+
req = rekor_v2.entry.CreateEntryRequest(
136+
dsse_request_v002=rekor_v2.dsse.DSSERequestV002(
137+
envelope=envelope._inner,
145138
verifiers=[
146-
v2.Verifier(
139+
rekor_v2.verifier.Verifier(
147140
x509_certificate=common_v1.X509Certificate(
148-
raw_bytes=certificate.public_bytes(
149-
encoding=serialization.Encoding.DER
141+
raw_bytes=base64.b64encode(
142+
certificate.public_bytes(
143+
encoding=serialization.Encoding.DER
144+
)
150145
)
151146
),
152147
key_details=_get_key_details(certificate),

sigstore/models.py

Lines changed: 0 additions & 227 deletions
Original file line numberDiff line numberDiff line change
@@ -218,233 +218,6 @@ def _verify(self, keyring: RekorKeyring) -> None:
218218
)
219219

220220

221-
# @dataclass(frozen=True)
222-
# class LogEntry:
223-
# """
224-
# Represents a transparency log entry.
225-
226-
# Log entries are retrieved from the transparency log after signing or verification events,
227-
# or loaded from "Sigstore" bundles provided by the user.
228-
229-
# This representation allows for either a missing inclusion promise or a missing
230-
# inclusion proof, but not both: attempting to construct a `LogEntry` without
231-
# at least one will fail.
232-
# """
233-
234-
# uuid: Optional[str] # noqa: UP045
235-
# """
236-
# This entry's unique ID in the log instance it was retrieved from.
237-
238-
# For sharded log deployments, IDs are unique per-shard.
239-
240-
# Not present for `LogEntry` instances loaded from Sigstore bundles.
241-
# """
242-
243-
# body: B64Str
244-
# """
245-
# The base64-encoded body of the transparency log entry.
246-
# """
247-
248-
# integrated_time: int
249-
# """
250-
# The UNIX time at which this entry was integrated into the transparency log.
251-
# """
252-
253-
# log_id: str
254-
# """
255-
# The log's ID (as the SHA256 hash of the DER-encoded public key for the log
256-
# at the time of entry inclusion).
257-
# """
258-
259-
# log_index: int
260-
# """
261-
# The index of this entry within the log.
262-
# """
263-
264-
# inclusion_proof: LogInclusionProof
265-
# """
266-
# An inclusion proof for this log entry.
267-
# """
268-
269-
# inclusion_promise: Optional[B64Str] # noqa: UP045
270-
# """
271-
# An inclusion promise for this log entry, if present.
272-
273-
# Internally, this is a base64-encoded Signed Entry Timestamp (SET) for this
274-
# log entry.
275-
# """
276-
277-
# _kind_version: KindVersion
278-
# """
279-
# The kind and version of the log entry.
280-
# """
281-
282-
# @classmethod
283-
# def _from_response(cls, dict_: dict[str, Any]) -> LogEntry:
284-
# """
285-
# Create a new `LogEntry` from the given API response.
286-
# """
287-
288-
# # Assumes we only get one entry back
289-
# entries = list(dict_.items())
290-
# if len(entries) != 1:
291-
# raise ValueError("Received multiple entries in response")
292-
# uuid, entry = entries[0]
293-
294-
# # Fill in the appropriate kind
295-
# body_entry: ProposedEntry = TypeAdapter(ProposedEntry).validate_json(
296-
# base64.b64decode(entry["body"])
297-
# )
298-
# if not isinstance(body_entry, (Hashedrekord, Dsse)):
299-
# raise InvalidBundle("log entry is not of expected type")
300-
301-
# return LogEntry(
302-
# uuid=uuid,
303-
# body=entry["body"],
304-
# integrated_time=entry["integratedTime"],
305-
# log_id=entry["logID"],
306-
# log_index=entry["logIndex"],
307-
# inclusion_proof=LogInclusionProof.model_validate(
308-
# entry["verification"]["inclusionProof"]
309-
# ),
310-
# inclusion_promise=entry["verification"]["signedEntryTimestamp"],
311-
# _kind_version=KindVersion(
312-
# kind=body_entry.kind, version=body_entry.api_version
313-
# ),
314-
# )
315-
316-
# @classmethod
317-
# def _from_dict_rekor(cls, dict_: dict[str, Any]) -> LogEntry:
318-
# """
319-
# Create a new `LogEntry` from the given Rekor TransparencyLogEntry.
320-
# """
321-
# tlog_entry = rekor_v1.TransparencyLogEntry.from_dict(dict_)
322-
323-
# inclusion_proof: InclusionProof | None = tlog_entry.inclusion_proof
324-
# # This check is required by us as the client, not the
325-
# # protobuf-specs themselves.
326-
# if not inclusion_proof or not inclusion_proof.checkpoint.envelope:
327-
# raise InvalidBundle("entry must contain inclusion proof, with checkpoint")
328-
329-
# parsed_inclusion_proof = LogInclusionProof(
330-
# checkpoint=inclusion_proof.checkpoint.envelope,
331-
# hashes=[h.hex() for h in inclusion_proof.hashes],
332-
# log_index=inclusion_proof.log_index,
333-
# root_hash=inclusion_proof.root_hash.hex(),
334-
# tree_size=inclusion_proof.tree_size,
335-
# )
336-
337-
# inclusion_promise: B64Str | None = None
338-
# if tlog_entry.inclusion_promise:
339-
# inclusion_promise = B64Str(
340-
# base64.b64encode(
341-
# tlog_entry.inclusion_promise.signed_entry_timestamp
342-
# ).decode()
343-
# )
344-
345-
# return LogEntry(
346-
# uuid=None,
347-
# body=B64Str(base64.b64encode(tlog_entry.canonicalized_body).decode()),
348-
# integrated_time=tlog_entry.integrated_time,
349-
# log_id=tlog_entry.log_id.key_id.hex(),
350-
# log_index=tlog_entry.log_index,
351-
# inclusion_proof=parsed_inclusion_proof,
352-
# _kind_version=tlog_entry.kind_version,
353-
# inclusion_promise=inclusion_promise,
354-
# )
355-
356-
# def _to_rekor(self) -> rekor_v1.TransparencyLogEntry:
357-
# """
358-
# Create a new protobuf-level `TransparencyLogEntry` from this `LogEntry`.
359-
360-
# @private
361-
# """
362-
# inclusion_proof = rekor_v1.InclusionProof(
363-
# log_index=self.inclusion_proof.log_index,
364-
# root_hash=bytes.fromhex(self.inclusion_proof.root_hash),
365-
# tree_size=self.inclusion_proof.tree_size,
366-
# hashes=[bytes.fromhex(hash_) for hash_ in self.inclusion_proof.hashes],
367-
# checkpoint=rekor_v1.Checkpoint(envelope=self.inclusion_proof.checkpoint),
368-
# )
369-
370-
# tlog_entry = rekor_v1.TransparencyLogEntry(
371-
# log_index=self.log_index,
372-
# log_id=common_v1.LogId(key_id=bytes.fromhex(self.log_id)),
373-
# integrated_time=self.integrated_time,
374-
# inclusion_proof=inclusion_proof,
375-
# kind_version=self._kind_version,
376-
# canonicalized_body=base64.b64decode(self.body),
377-
# )
378-
# if self.inclusion_promise:
379-
# inclusion_promise = rekor_v1.InclusionPromise(
380-
# signed_entry_timestamp=base64.b64decode(self.inclusion_promise)
381-
# )
382-
# tlog_entry.inclusion_promise = inclusion_promise
383-
384-
# return tlog_entry
385-
386-
# def encode_canonical(self) -> bytes:
387-
# """
388-
# Returns a canonicalized JSON (RFC 8785) representation of the transparency log entry.
389-
390-
# This encoded representation is suitable for verification against
391-
# the Signed Entry Timestamp.
392-
# """
393-
# payload: dict[str, int | str] = {
394-
# "body": self.body,
395-
# "integratedTime": self.integrated_time,
396-
# "logID": self.log_id,
397-
# "logIndex": self.log_index,
398-
# }
399-
400-
# return rfc8785.dumps(payload)
401-
402-
# def _verify_set(self, keyring: RekorKeyring) -> None:
403-
# """
404-
# Verify the inclusion promise (Signed Entry Timestamp) for a given transparency log
405-
# `entry` using the given `keyring`.
406-
407-
# Fails if the given log entry does not contain an inclusion promise.
408-
# """
409-
410-
# if self.inclusion_promise is None:
411-
# raise VerificationError("SET: invalid inclusion promise: missing")
412-
413-
# signed_entry_ts = base64.b64decode(self.inclusion_promise)
414-
415-
# try:
416-
# keyring.verify(
417-
# key_id=KeyID(bytes.fromhex(self.log_id)),
418-
# signature=signed_entry_ts,
419-
# data=self.encode_canonical(),
420-
# )
421-
# except VerificationError as exc:
422-
# raise VerificationError(f"SET: invalid inclusion promise: {exc}")
423-
424-
# def _verify(self, keyring: RekorKeyring) -> None:
425-
# """
426-
# Verifies this log entry.
427-
428-
# This method performs steps (5), (6), and optionally (7) in
429-
# the top-level verify API:
430-
431-
# * Verifies the consistency of the entry with the given bundle;
432-
# * Verifies the Merkle inclusion proof and its signed checkpoint;
433-
# * Verifies the inclusion promise, if present.
434-
# """
435-
436-
# verify_merkle_inclusion(self)
437-
# verify_checkpoint(keyring, self)
438-
439-
# _logger.debug(f"successfully verified inclusion proof: index={self.log_index}")
440-
441-
# if self.inclusion_promise:
442-
# self._verify_set(keyring)
443-
# _logger.debug(
444-
# f"successfully verified inclusion promise: index={self.log_index}"
445-
# )
446-
447-
448221
class TimestampVerificationData:
449222
"""
450223
Represents a TimestampVerificationData structure.

sigstore/verify/verifier.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
from pydantic import ValidationError
4040
from rfc3161_client import TimeStampResponse, VerifierBuilder
4141
from rfc3161_client import VerificationError as Rfc3161VerificationError
42-
from sigstore_protobuf_specs.dev.sigstore.common import v1
43-
from sigstore_protobuf_specs.dev.sigstore.rekor import v2
42+
from sigstore_models.common import v1
43+
from sigstore_models.rekor import v2
4444

4545
from sigstore import dsse
4646
from sigstore._internal.rekor import _hashedrekord_from_parts
@@ -546,7 +546,7 @@ def _validate_dsse_v002_entry_body(bundle: Bundle) -> None:
546546
"cannot perform DSSE verification on a bundle without a DSSE envelope"
547547
)
548548
try:
549-
v2_body = v2.Entry().from_json(entry._inner.canonicalized_body)
549+
v2_body = v2.entry.Entry.from_json(entry._inner.canonicalized_body)
550550
except ValidationError as exc:
551551
raise VerificationError(f"invalid DSSE log entry: {exc}")
552552

@@ -561,8 +561,8 @@ def _validate_dsse_v002_entry_body(bundle: Bundle) -> None:
561561
raise VerificationError("DSSE entry payload hash does not match bundle")
562562

563563
v2_signatures = [
564-
v2.Signature(
565-
content=signature.sig,
564+
v2.verifier.Signature(
565+
content=base64.b64encode(signature.sig),
566566
verifier=_v2_verifier_from_certificate(bundle.signing_certificate),
567567
)
568568
for signature in envelope._inner.signatures
@@ -601,30 +601,32 @@ def _validate_hashedrekord_v002_entry_body(bundle: Bundle) -> None:
601601
raise VerificationError(
602602
"invalid hashedrekord log entry: missing message signature"
603603
)
604-
v2_expected_body = v2.Entry(
604+
v2_expected_body = v2.entry.Entry(
605605
kind=entry._inner.kind_version.kind,
606606
api_version=entry._inner.kind_version.version,
607-
spec=v2.Spec(
608-
hashed_rekord_v002=v2.HashedRekordLogEntryV002(
607+
spec=v2.entry.Spec(
608+
hashed_rekord_v002=v2.hashedrekord.HashedRekordLogEntryV002(
609609
data=v1.HashOutput(
610610
algorithm=bundle._inner.message_signature.message_digest.algorithm,
611-
digest=bundle._inner.message_signature.message_digest.digest,
611+
digest=base64.b64encode(
612+
bundle._inner.message_signature.message_digest.digest
613+
),
612614
),
613-
signature=v2.Signature(
614-
content=bundle._inner.message_signature.signature,
615+
signature=v2.verifier.Signature(
616+
content=base64.b64encode(bundle._inner.message_signature.signature),
615617
verifier=_v2_verifier_from_certificate(bundle.signing_certificate),
616618
),
617619
)
618620
),
619621
)
620-
v2_actual_body = v2.Entry().from_json(entry._inner.canonicalized_body)
622+
v2_actual_body = v2.entry.Entry.from_json(entry._inner.canonicalized_body)
621623
if v2_expected_body != v2_actual_body:
622624
raise VerificationError(
623625
"transparency log entry is inconsistent with other materials"
624626
)
625627

626628

627-
def _v2_verifier_from_certificate(certificate: Certificate) -> v2.Verifier:
629+
def _v2_verifier_from_certificate(certificate: Certificate) -> v2.verifier.Verifier:
628630
"""
629631
Return a Rekor v2 protobuf Verifier for the signing certificate.
630632
@@ -648,9 +650,11 @@ def _v2_verifier_from_certificate(certificate: Certificate) -> v2.Verifier:
648650
else:
649651
raise ValueError(f"Unsupported public key type: {type(public_key)}")
650652

651-
return v2.Verifier(
653+
return v2.verifier.Verifier(
652654
x509_certificate=v1.X509Certificate(
653-
certificate.public_bytes(encoding=serialization.Encoding.DER)
655+
raw_bytes=base64.b64encode(
656+
certificate.public_bytes(encoding=serialization.Encoding.DER)
657+
)
654658
),
655659
key_details=cast(v1.PublicKeyDetails, key_details),
656660
)

0 commit comments

Comments
 (0)