Skip to content

Commit 397e166

Browse files
committed
refactors and adding trusted_root to Verifier and SigningContext
Signed-off-by: Javan lacerda <javanlacerda@google.com>
1 parent f430317 commit 397e166

File tree

6 files changed

+79
-75
lines changed

6 files changed

+79
-75
lines changed

sigstore/_cli.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from textwrap import dedent
2424
from typing import NoReturn, Optional, TextIO, Union, cast
2525

26-
from cryptography.x509 import load_pem_x509_certificates
2726
from rich.logging import RichHandler
2827
from sigstore_protobuf_specs.dev.sigstore.bundle.v1 import Bundle
2928

@@ -651,17 +650,20 @@ def _sign(args: argparse.Namespace) -> None:
651650
else:
652651
# Assume "production" trust root if no keys are given as arguments
653652
trusted_root = TrustedRoot.production()
653+
trusted_root.set_args(args)
654654
if args.ctfe_pem is not None:
655655
ctfe_keys = [args.ctfe_pem.read()]
656656
else:
657657
ctfe_keys = trusted_root.get_ctfe_keys()
658658

659-
ct_keyring = CTKeyring(Keyring(ctfe_keys))
660659
rekor_keyring = trusted_root.rekor_keyring(KeyringPurpose.SIGN)
661660

661+
ct_keyring = CTKeyring(Keyring(ctfe_keys))
662+
662663
signing_ctx = SigningContext(
663664
fulcio=FulcioClient(args.fulcio_url),
664665
rekor=RekorClient(args.rekor_url, rekor_keyring, ct_keyring),
666+
trusted_root=trusted_root,
665667
)
666668

667669
# The order of precedence for identities is as follows:
@@ -809,24 +811,17 @@ def _collect_verification_state(
809811
args,
810812
f"Missing verification materials for {(file)}: {', '.join(missing)}",
811813
)
812-
purpose = KeyringPurpose.VERIFY
813814
if args.staging:
814815
logger.debug("verify: staging instances requested")
815-
verifier = Verifier.staging(purpose)
816+
verifier = Verifier.staging()
816817
elif args.rekor_url == DEFAULT_REKOR_URL:
817-
verifier = Verifier.production(purpose)
818+
verifier = Verifier.production()
818819
else:
819820
if not args.certificate_chain:
820821
_die(args, "Custom Rekor URL used without specifying --certificate-chain")
821822

822-
try:
823-
certificate_chain = load_pem_x509_certificates(
824-
args.certificate_chain.read()
825-
)
826-
except ValueError as error:
827-
_die(args, f"Invalid certificate chain: {error}")
828-
829823
trusted_root = TrustedRoot.production()
824+
trusted_root.set_args(args=args)
830825
ct_keys = trusted_root.get_ctfe_keys()
831826

832827
verifier = Verifier(
@@ -835,7 +830,7 @@ def _collect_verification_state(
835830
rekor_keyring=trusted_root.rekor_keyring(KeyringPurpose.VERIFY),
836831
ct_keyring=CTKeyring(Keyring(ct_keys)),
837832
),
838-
fulcio_certificate_chain=certificate_chain,
833+
trusted_root=trusted_root,
839834
)
840835

841836
all_materials = []

sigstore/_internal/trustroot.py

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@
1818

1919
from __future__ import annotations
2020

21+
from argparse import Namespace
2122
from datetime import datetime, timezone
2223
from enum import Enum
2324
from pathlib import Path
24-
from typing import Iterable, NewType
25+
from typing import Iterable, NewType, Optional
2526

26-
from cryptography.x509 import Certificate, load_der_x509_certificate
27+
from cryptography.x509 import (
28+
Certificate,
29+
load_der_x509_certificate,
30+
load_pem_x509_certificates,
31+
)
2732
from sigstore_protobuf_specs.dev.sigstore.common.v1 import TimeRange
2833
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
2934
CertificateAuthority,
@@ -67,8 +72,8 @@ class KeyringPurpose(str, Enum):
6772
Keyring purpose typing
6873
"""
6974

70-
SIGN = "Signing"
71-
VERIFY = "Verifying"
75+
SIGN = "sign"
76+
VERIFY = "verify"
7277

7378
def __str__(self) -> str:
7479
"""Returns the purpose string value."""
@@ -78,6 +83,9 @@ def __str__(self) -> str:
7883
class TrustedRoot(_TrustedRoot):
7984
"""Complete set of trusted entities for a Sigstore client"""
8085

86+
def __init__(self, args: Optional[Namespace] = None):
87+
self.args = args
88+
8189
@classmethod
8290
def from_file(cls, path: str) -> "TrustedRoot":
8391
"""Create a new trust root from file"""
@@ -114,9 +122,10 @@ def staging(cls, offline: bool = False) -> "TrustedRoot":
114122

115123
@staticmethod
116124
def _get_tlog_keys(
117-
tlogs: list[TransparencyLogInstance], allow_expired: bool
125+
tlogs: list[TransparencyLogInstance], purpose: KeyringPurpose
118126
) -> Iterable[bytes]:
119127
"""Return public key contents given transparency log instances."""
128+
allow_expired = purpose is KeyringPurpose.VERIFY
120129
for key in tlogs:
121130
if not _is_timerange_valid(
122131
key.public_key.valid_for, allow_expired=allow_expired
@@ -138,33 +147,33 @@ def _get_ca_keys(
138147
for cert in ca.cert_chain.certificates:
139148
yield cert.raw_bytes
140149

150+
def set_args(self, args: Namespace) -> None:
151+
self.args = args
152+
141153
def rekor_keyring(self, purpose: KeyringPurpose) -> RekorKeyring:
142154
"""Return public key contents given certificate authorities."""
143155

144-
if purpose is KeyringPurpose.VERIFY:
145-
allow_expired = True
146-
elif purpose is KeyringPurpose.SIGN:
147-
allow_expired = False
148-
else:
149-
# TODO(javan): Create a specific exception
150-
raise Exception()
151-
152-
return RekorKeyring(Keyring(self._get_rekor_keys(allow_expired)))
156+
return RekorKeyring(self._get_rekor_keys(purpose))
153157

154158
def get_ctfe_keys(self) -> list[bytes]:
155159
"""Return the CTFE public keys contents."""
156-
ctfes: list[bytes] = list(self._get_tlog_keys(self.ctlogs, True))
160+
# TODO: get purpose as argument
161+
purpose = KeyringPurpose.VERIFY
162+
ctfes: list[bytes] = list(self._get_tlog_keys(self.ctlogs, purpose))
157163
if not ctfes:
158164
raise MetadataError("CTFE keys not found in trusted root")
159165
return ctfes
160166

161-
def _get_rekor_keys(self, allow_expired: bool) -> list[bytes]:
167+
def _get_rekor_keys(self, purpose: KeyringPurpose) -> Keyring:
162168
"""Return the rekor public key content."""
163-
keys: list[bytes] = list(self._get_tlog_keys(self.tlogs, allow_expired))
164-
169+
keys: list[bytes]
170+
if self.args and self.args.rekor_root_pubkey:
171+
keys = self.args.rekor_root_pubkey.read()
172+
else:
173+
keys = list(self._get_tlog_keys(self.tlogs, purpose))
165174
if len(keys) != 1:
166175
raise MetadataError("Did not find one Rekor key in trusted root")
167-
return keys
176+
return Keyring(keys)
168177

169178
def get_fulcio_certs(self) -> list[Certificate]:
170179
"""Return the Fulcio certificates."""
@@ -173,11 +182,20 @@ def get_fulcio_certs(self) -> list[Certificate]:
173182

174183
# Return expired certificates too: they are expired now but may have
175184
# been active when the certificate was used to sign.
176-
certs = [
177-
load_der_x509_certificate(c)
178-
for c in self._get_ca_keys(self.certificate_authorities, allow_expired=True)
179-
]
180185

186+
if self.args:
187+
try:
188+
certs = load_pem_x509_certificates(self.args.certificate_chain.read())
189+
except ValueError as error:
190+
self.args._parser.error(f"Invalid certificate chain: {error}")
191+
raise ValueError("unreachable")
192+
else:
193+
certs = [
194+
load_der_x509_certificate(c)
195+
for c in self._get_ca_keys(
196+
self.certificate_authorities, allow_expired=True
197+
)
198+
]
181199
if not certs:
182200
raise MetadataError("Fulcio certificates not found in trusted root")
183201
return certs

sigstore/sign.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,7 @@ class SigningContext:
280280
"""
281281

282282
def __init__(
283-
self,
284-
*,
285-
fulcio: FulcioClient,
286-
rekor: RekorClient,
283+
self, *, fulcio: FulcioClient, rekor: RekorClient, trusted_root: TrustedRoot
287284
):
288285
"""
289286
Create a new `SigningContext`.
@@ -296,29 +293,28 @@ def __init__(
296293
"""
297294
self._fulcio = fulcio
298295
self._rekor = rekor
296+
self._trusted_root = trusted_root
299297

300298
@classmethod
301299
def production(cls) -> SigningContext:
302300
"""
303301
Return a `SigningContext` instance configured against Sigstore's production-level services.
304302
"""
305-
trust_root = TrustedRoot.production()
306-
rekor = RekorClient.production(trust_root, purpose=KeyringPurpose.SIGN)
303+
trusted_root = TrustedRoot.production()
304+
rekor = RekorClient.production(trusted_root, purpose=KeyringPurpose.SIGN)
307305
return cls(
308-
fulcio=FulcioClient.production(),
309-
rekor=rekor,
306+
fulcio=FulcioClient.production(), rekor=rekor, trusted_root=trusted_root
310307
)
311308

312309
@classmethod
313310
def staging(cls) -> SigningContext:
314311
"""
315312
Return a `SignerContext` instance configured against Sigstore's staging-level services.
316313
"""
317-
trust_root = TrustedRoot.staging()
318-
rekor = RekorClient.staging(trust_root, purpose=KeyringPurpose.SIGN)
314+
trusted_root = TrustedRoot.staging()
315+
rekor = RekorClient.staging(trusted_root, purpose=KeyringPurpose.SIGN)
319316
return cls(
320-
fulcio=FulcioClient.staging(),
321-
rekor=rekor,
317+
fulcio=FulcioClient.staging(), rekor=rekor, trusted_root=trusted_root
322318
)
323319

324320
@contextmanager

sigstore/verify/verifier.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
from cryptography.exceptions import InvalidSignature
2727
from cryptography.hazmat.primitives.asymmetric import ec
2828
from cryptography.x509 import (
29-
Certificate,
3029
ExtendedKeyUsage,
3130
KeyUsage,
3231
)
@@ -110,9 +109,7 @@ class Verifier:
110109
The primary API for verification operations.
111110
"""
112111

113-
def __init__(
114-
self, *, rekor: RekorClient, fulcio_certificate_chain: List[Certificate]
115-
):
112+
def __init__(self, *, rekor: RekorClient, trusted_root: TrustedRoot):
116113
"""
117114
Create a new `Verifier`.
118115
@@ -125,29 +122,29 @@ def __init__(
125122
self._rekor = rekor
126123
self._fulcio_certificate_chain: List[X509] = [
127124
X509.from_cryptography(parent_cert)
128-
for parent_cert in fulcio_certificate_chain
125+
for parent_cert in trusted_root.get_fulcio_certs()
129126
]
130127

131128
@classmethod
132-
def production(cls, purpose: KeyringPurpose) -> Verifier:
129+
def production(cls) -> Verifier:
133130
"""
134131
Return a `Verifier` instance configured against Sigstore's production-level services.
135132
"""
136-
trust_root = TrustedRoot.production()
133+
trusted_root = TrustedRoot.production()
137134
return cls(
138-
rekor=RekorClient.production(trust_root, purpose),
139-
fulcio_certificate_chain=trust_root.get_fulcio_certs(),
135+
rekor=RekorClient.production(trusted_root, KeyringPurpose.VERIFY),
136+
trusted_root=trusted_root,
140137
)
141138

142139
@classmethod
143-
def staging(cls, purpose: KeyringPurpose) -> Verifier:
140+
def staging(cls) -> Verifier:
144141
"""
145142
Return a `Verifier` instance configured against Sigstore's staging-level services.
146143
"""
147-
trust_root = TrustedRoot.staging()
144+
trusted_root = TrustedRoot.staging()
148145
return cls(
149-
rekor=RekorClient.staging(trust_root, purpose),
150-
fulcio_certificate_chain=trust_root.get_fulcio_certs(),
146+
rekor=RekorClient.staging(trusted_root, KeyringPurpose.VERIFY),
147+
trusted_root=trusted_root,
151148
)
152149

153150
def verify(

test/unit/test_sign.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import sigstore.oidc
2424
from sigstore._internal.keyring import KeyringError, KeyringLookupError
2525
from sigstore._internal.sct import InvalidSCTError, InvalidSCTKeyError
26-
from sigstore._internal.trustroot import KeyringPurpose
2726
from sigstore.dsse import _StatementBuilder, _Subject
2827
from sigstore.hashes import Hashed
2928
from sigstore.sign import SigningContext
@@ -131,7 +130,7 @@ def test_sign_prehashed(staging):
131130
sign_ctx, verifier, identity = staging
132131

133132
sign_ctx: SigningContext = sign_ctx()
134-
verifier: Verifier = verifier(KeyringPurpose.VERIFY)
133+
verifier: Verifier = verifier()
135134

136135
input_ = secrets.token_bytes(32)
137136
hashed = Hashed(

0 commit comments

Comments
 (0)