Skip to content

Commit c546aea

Browse files
Merge branch 'main' into rekov2-client
2 parents e6a6fe3 + 6f52d7c commit c546aea

File tree

47 files changed

+776
-856
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+776
-856
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ jobs:
7878
7979
# Ensure Timestamp Authority tests are not skipped by
8080
# having pytest show skipped tests and verifying ours are running
81+
set -o pipefail
8182
make test TEST_ARGS="-m timestamp_authority -rs" | tee output
8283
! grep -q "skipping test that requires a Timestamp Authority" output || (echo "ERROR: Found skip message" && exit 1)
8384
env:

CHANGELOG.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ All versions prior to 0.9.0 are untracked.
1010

1111
### Added
1212

13+
* Added `LogEntry._kind_version`, which is now parsed earlier upon receipt from the rekor API,
14+
either from the root of the response, or from the reponse's inner base64-encoded JSON `body`.
15+
[#1370](https://github.com/sigstore/sigstore-python/pull/1370)
16+
1317
* Added support for ed25519 keys.
1418
[#1377](https://github.com/sigstore/sigstore-python/pull/1377)
1519

@@ -22,11 +26,11 @@ All versions prior to 0.9.0 are untracked.
2226
* TSA: Changed the Timestamp Authority requests to explicitly use sha256 for message digests.
2327
[#1373](https://github.com/sigstore/sigstore-python/pull/1373)
2428

25-
* Fixed the certificate calidity period check for Timestamp Authorities (TSA).
26-
Certificates need not have and end date, while still requiring a start date.
29+
* Fixed the certificate validity period check for Timestamp Authorities (TSA).
30+
Certificates need not have an end date, while still requiring a start date.
2731
[#1368](https://github.com/sigstore/sigstore-python/pull/1368)
2832

29-
* API: Make Rekor APIs compatible with Rekor v2 by removing trailing slashes
33+
* Made Rekor client more compatible with Rekor v2 by removing trailing slashes
3034
from endpoints ([#1366](https://github.com/sigstore/sigstore-python/pull/1366))
3135

3236
* Verify: verify that all established times (timestamps or the log integration time)
@@ -38,10 +42,31 @@ All versions prior to 0.9.0 are untracked.
3842
[sigstore/timestamp-authority](https://github.com/sigstore/timestamp-authority)
3943
[#1377](https://github.com/sigstore/sigstore-python/pull/1377)
4044

45+
* Tests: Updated the `staging` and `sign_ctx_and_ident_for_env` fixtures to use the new methods
46+
for generating a `SigningContext`.
47+
[#1409](https://github.com/sigstore/sigstore-python/pull/1409)
48+
4149
### Changed
4250

51+
* API:
52+
* ClientTrustConfig now provides methods `production()`, `staging()`and `from_tuf()`
53+
to get access to current client configuration (trusted keys & certificates,
54+
URLs and their validity periods). [#1363](https://github.com/sigstore/sigstore-python/pull/1363)
4355
* `--trust-config` now requires a file with SigningConfig v0.2, and is able to fully
4456
configure the used Sigstore instance [#1358]/(https://github.com/sigstore/sigstore-python/pull/1358)
57+
* By default (when `--trust-config` is not used) the whole trust configuration now
58+
comes from the TUF repository [#1363](https://github.com/sigstore/sigstore-python/pull/1363)
59+
60+
### Removed
61+
* API:
62+
* `Issuer.production()` and `Issuer.staging()` have been removed: Use
63+
`Issuer()` instead with relevant URL. The current public good production and
64+
staging URLs are available via the `ClientTrustConfig` object.
65+
[#1363](https://github.com/sigstore/sigstore-python/pull/1363)
66+
* `SigningContext.production()` and `SigningContext.staging()` have been removed:
67+
Use `SigningContext.from_trust_config()` instead.
68+
[#1363](https://github.com/sigstore/sigstore-python/pull/1363)
69+
4570

4671
## [3.6.2]
4772

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,6 @@ update-embedded-root: $(VENV)/pyvenv.cfg
177177
. $(VENV_BIN)/activate && \
178178
python -m sigstore plumbing update-trust-root
179179
cp ~/.local/share/sigstore-python/tuf/https%3A%2F%2Ftuf-repo-cdn.sigstore.dev/root.json \
180-
sigstore/_store/prod/root.json
180+
sigstore/_store/https%3A%2F%2Ftuf-repo-cdn.sigstore.dev/root.json
181181
cp ~/.cache/sigstore-python/tuf/https%3A%2F%2Ftuf-repo-cdn.sigstore.dev/trusted_root.json \
182-
sigstore/_store/prod/trusted_root.json
182+
sigstore/_store/https%3A%2F%2Ftuf-repo-cdn.sigstore.dev/trusted_root.json

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,7 @@ OpenID Connect options:
121121
--oidc-disable-ambient-providers
122122
Disable ambient OpenID Connect credential detection
123123
(e.g. on GitHub Actions) (default: False)
124-
--oidc-issuer URL The OpenID Connect issuer to use (conflicts with
125-
--staging) (default: https://oauth2.sigstore.dev/auth)
124+
--oidc-issuer URL The OpenID Connect issuer to use (default: None)
126125
--oauth-force-oob Force an out-of-band OAuth flow and do not
127126
automatically start the default web browser (default:
128127
False)
@@ -185,8 +184,7 @@ OpenID Connect options:
185184
--oidc-disable-ambient-providers
186185
Disable ambient OpenID Connect credential detection
187186
(e.g. on GitHub Actions) (default: False)
188-
--oidc-issuer URL The OpenID Connect issuer to use (conflicts with
189-
--staging) (default: https://oauth2.sigstore.dev/auth)
187+
--oidc-issuer URL The OpenID Connect issuer to use (default: None)
190188
--oauth-force-oob Force an out-of-band OAuth flow and do not
191189
automatically start the default web browser (default:
192190
False)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ lint = [
6262
"mypy ~= 1.1",
6363
# NOTE(ww): ruff is under active development, so we pin conservatively here
6464
# and let Dependabot periodically perform this update.
65-
"ruff < 0.11.11",
65+
"ruff < 0.11.12",
6666
"types-requests",
6767
"types-pyOpenSSL",
6868
]

sigstore/_cli.py

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from sigstore._internal.fulcio.client import ExpiredCertificate
4040
from sigstore._internal.rekor import _hashedrekord_from_parts
4141
from sigstore._internal.rekor.client import RekorClient
42-
from sigstore._internal.trust import ClientTrustConfig, TrustedRoot
42+
from sigstore._internal.trust import ClientTrustConfig
4343
from sigstore._utils import sha256_digest
4444
from sigstore.dsse import StatementBuilder, Subject
4545
from sigstore.dsse._predicate import (
@@ -51,7 +51,6 @@
5151
from sigstore.hashes import Hashed
5252
from sigstore.models import Bundle, InvalidBundle
5353
from sigstore.oidc import (
54-
DEFAULT_OAUTH_ISSUER_URL,
5554
ExpiredIdentity,
5655
IdentityToken,
5756
Issuer,
@@ -229,8 +228,8 @@ def _add_shared_oidc_options(
229228
"--oidc-issuer",
230229
metavar="URL",
231230
type=str,
232-
default=os.getenv("SIGSTORE_OIDC_ISSUER", DEFAULT_OAUTH_ISSUER_URL),
233-
help="The OpenID Connect issuer to use (conflicts with --staging)",
231+
default=os.getenv("SIGSTORE_OIDC_ISSUER", None),
232+
help="The OpenID Connect issuer to use",
234233
)
235234
group.add_argument(
236235
"--oauth-force-oob",
@@ -614,11 +613,7 @@ def main(args: list[str] | None = None) -> None:
614613
elif args.verify_subcommand == "github":
615614
_verify_github(args)
616615
elif args.subcommand == "get-identity-token":
617-
identity = _get_identity(args)
618-
if identity:
619-
print(identity)
620-
else:
621-
_invalid_arguments(args, "No identity token supplied or detected!")
616+
_get_identity_token(args)
622617
elif args.subcommand == "plumbing":
623618
if args.plumbing_subcommand == "fix-bundle":
624619
_fix_bundle(args)
@@ -630,6 +625,17 @@ def main(args: list[str] | None = None) -> None:
630625
e.log_and_exit(_logger, args.verbose >= 1)
631626

632627

628+
def _get_identity_token(args: argparse.Namespace) -> None:
629+
"""
630+
Output the OIDC authentication token
631+
"""
632+
identity = _get_identity(args, _get_trust_config(args))
633+
if identity:
634+
print(identity)
635+
else:
636+
_invalid_arguments(args, "No identity token supplied or detected!")
637+
638+
633639
def _sign_common(
634640
args: argparse.Namespace, output_map: OutputMap, predicate: dict[str, Any] | None
635641
) -> None:
@@ -643,17 +649,8 @@ def _sign_common(
643649
not, it will use a hashedrekord.
644650
"""
645651
# Select the signing context to use.
646-
if args.staging:
647-
_logger.debug("sign: staging instances requested")
648-
signing_ctx = SigningContext.staging()
649-
elif args.trust_config:
650-
trust_config = ClientTrustConfig.from_json(args.trust_config.read_text())
651-
signing_ctx = SigningContext._from_trust_config(trust_config)
652-
else:
653-
# If the user didn't request the staging instance or pass in an
654-
# explicit client trust config, we're using the public good (i.e.
655-
# production) instance.
656-
signing_ctx = SigningContext.production()
652+
trust_config = _get_trust_config(args)
653+
signing_ctx = SigningContext.from_trust_config(trust_config)
657654

658655
# The order of precedence for identities is as follows:
659656
#
@@ -664,7 +661,7 @@ def _sign_common(
664661
if args.identity_token:
665662
identity = IdentityToken(args.identity_token)
666663
else:
667-
identity = _get_identity(args)
664+
identity = _get_identity(args, trust_config)
668665

669666
if not identity:
670667
_invalid_arguments(args, "No identity token supplied or detected!")
@@ -1009,14 +1006,8 @@ def _collect_verification_state(
10091006
f"Missing verification materials for {(hashed)}: {', '.join(missing)}",
10101007
)
10111008

1012-
if args.staging:
1013-
_logger.debug("verify: staging instances requested")
1014-
verifier = Verifier.staging(offline=args.offline)
1015-
elif args.trust_config:
1016-
trust_config = ClientTrustConfig.from_json(args.trust_config.read_text())
1017-
verifier = Verifier._from_trust_config(trust_config)
1018-
else:
1019-
verifier = Verifier.production(offline=args.offline)
1009+
trust_config = _get_trust_config(args)
1010+
verifier = Verifier(trusted_root=trust_config.trusted_root)
10201011

10211012
all_materials = []
10221013
for file_or_hashed, materials in input_map.items():
@@ -1167,7 +1158,27 @@ def _verify_common(
11671158
return None
11681159

11691160

1170-
def _get_identity(args: argparse.Namespace) -> Optional[IdentityToken]:
1161+
def _get_trust_config(args: argparse.Namespace) -> ClientTrustConfig:
1162+
"""
1163+
Return the client trust configuration (Sigstore service URLs, key material and lifetimes)
1164+
1165+
The configuration may come from explicit argument (--trust-config) or from the TUF
1166+
repository of the used Sigstore instance.
1167+
"""
1168+
# Not all commands provide --offline
1169+
offline = getattr(args, "offline", False)
1170+
1171+
if args.trust_config:
1172+
return ClientTrustConfig.from_json(args.trust_config.read_text())
1173+
elif args.staging:
1174+
return ClientTrustConfig.staging(offline=offline)
1175+
else:
1176+
return ClientTrustConfig.production(offline=offline)
1177+
1178+
1179+
def _get_identity(
1180+
args: argparse.Namespace, trust_config: ClientTrustConfig
1181+
) -> Optional[IdentityToken]:
11711182
token = None
11721183
if not args.oidc_disable_ambient_providers:
11731184
token = detect_credential()
@@ -1176,12 +1187,10 @@ def _get_identity(args: argparse.Namespace) -> Optional[IdentityToken]:
11761187
if token:
11771188
return IdentityToken(token)
11781189

1179-
if args.staging:
1180-
issuer = Issuer.staging()
1181-
elif args.oidc_issuer == DEFAULT_OAUTH_ISSUER_URL:
1182-
issuer = Issuer.production()
1183-
else:
1190+
if args.oidc_issuer is not None:
11841191
issuer = Issuer(args.oidc_issuer)
1192+
else:
1193+
issuer = Issuer(trust_config.signing_config.get_oidc_url())
11851194

11861195
if args.oidc_client_secret is None:
11871196
args.oidc_client_secret = "" # nosec: B105
@@ -1198,6 +1207,7 @@ def _get_identity(args: argparse.Namespace) -> Optional[IdentityToken]:
11981207
def _fix_bundle(args: argparse.Namespace) -> None:
11991208
# NOTE: We could support `--trusted-root` here in the future,
12001209
# for custom Rekor instances.
1210+
12011211
rekor = RekorClient.staging() if args.staging else RekorClient.production()
12021212

12031213
raw_bundle = RawBundle.from_dict(json.loads(args.bundle.read_bytes()))
@@ -1234,13 +1244,10 @@ def _fix_bundle(args: argparse.Namespace) -> None:
12341244

12351245

12361246
def _update_trust_root(args: argparse.Namespace) -> None:
1237-
# Simply creating the TrustedRoot in online mode is enough to perform
1247+
# Simply creating the TrustConfig in online mode is enough to perform
12381248
# a metadata update.
1239-
if args.staging:
1240-
trusted_root = TrustedRoot.staging(offline=False)
1241-
else:
1242-
trusted_root = TrustedRoot.production(offline=False)
12431249

1250+
config = _get_trust_config(args)
12441251
_console.print(
1245-
f"Trust root updated: {len(trusted_root.get_fulcio_certs())} Fulcio certificates"
1252+
f"Trust root & signing config updated: {len(config.trusted_root.get_fulcio_certs())} Fulcio certificates"
12461253
)

sigstore/_internal/fulcio/client.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@
3939

4040
_logger = logging.getLogger(__name__)
4141

42-
DEFAULT_FULCIO_URL = "https://fulcio.sigstore.dev"
43-
STAGING_FULCIO_URL = "https://fulcio.sigstage.dev"
4442
SIGNING_CERT_ENDPOINT = "/api/v2/signingCert"
4543
TRUST_BUNDLE_ENDPOINT = "/api/v2/trustBundle"
4644

@@ -163,7 +161,7 @@ def get(self) -> FulcioTrustBundleResponse:
163161
class FulcioClient:
164162
"""The internal Fulcio client"""
165163

166-
def __init__(self, url: str = DEFAULT_FULCIO_URL) -> None:
164+
def __init__(self, url: str) -> None:
167165
"""Initialize the client"""
168166
_logger.debug(f"Fulcio client using URL: {url}")
169167
self.url = url
@@ -180,20 +178,6 @@ def __del__(self) -> None:
180178
"""
181179
self.session.close()
182180

183-
@classmethod
184-
def production(cls) -> FulcioClient:
185-
"""
186-
Returns a `FulcioClient` for the Sigstore production instance of Fulcio.
187-
"""
188-
return cls(DEFAULT_FULCIO_URL)
189-
190-
@classmethod
191-
def staging(cls) -> FulcioClient:
192-
"""
193-
Returns a `FulcioClient` for the Sigstore staging instance of Fulcio.
194-
"""
195-
return cls(STAGING_FULCIO_URL)
196-
197181
@property
198182
def signing_cert(self) -> FulcioSigningCert:
199183
"""

0 commit comments

Comments
 (0)