Skip to content

Commit 8ec76df

Browse files
committed
feat: Expose ClientTrustConfig as a public class
Signed-off-by: SequeI <[email protected]>
1 parent 249ba1d commit 8ec76df

File tree

10 files changed

+175
-132
lines changed

10 files changed

+175
-132
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ All versions prior to 0.9.0 are untracked.
6565
* SigningConfig now has methods that return actual clients (like `RekorClient`) instead of
6666
just URLs. The returned clients are also filtered according to SigningConfig contents.
6767
[#1407](https://github.com/sigstore/sigstore-python/pull/1407)
68+
* The ClientTrustConfig class has been moved from the private _internal package to a new public
69+
module (sigstore.trust). This change formally adds the class to the project's public API,
70+
making it available for use in other projects.
6871
* `--trust-config` now requires a file with SigningConfig v0.2, and is able to fully
6972
configure the used Sigstore instance [#1358]/(https://github.com/sigstore/sigstore-python/pull/1358)
7073
* By default (when `--trust-config` is not used) the whole trust configuration now

docs/api/trust.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:::sigstore.trust

sigstore/_cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
from sigstore._internal.fulcio.client import ExpiredCertificate
4141
from sigstore._internal.rekor import _hashedrekord_from_parts
4242
from sigstore._internal.rekor.client import RekorClient
43-
from sigstore._internal.trust import ClientTrustConfig
4443
from sigstore._utils import sha256_digest
4544
from sigstore.dsse import StatementBuilder, Subject
4645
from sigstore.dsse._predicate import (
@@ -58,6 +57,7 @@
5857
detect_credential,
5958
)
6059
from sigstore.sign import Signer, SigningContext
60+
from sigstore.trust import ClientTrustConfig
6161
from sigstore.verify import (
6262
Verifier,
6363
policy,

sigstore/_internal/trust.py

Lines changed: 1 addition & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@
4343
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
4444
CertificateAuthority as _CertificateAuthority,
4545
)
46-
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
47-
ClientTrustConfig as _ClientTrustConfig,
48-
)
4946
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
5047
Service,
5148
ServiceConfiguration,
@@ -64,15 +61,13 @@
6461
from sigstore._internal.rekor.client import RekorClient
6562
from sigstore._internal.rekor.client_v2 import RekorV2Client
6663
from sigstore._internal.timestamp import TimestampAuthorityClient
67-
from sigstore._internal.tuf import DEFAULT_TUF_URL, STAGING_TUF_URL, TrustUpdater
6864
from sigstore._utils import (
6965
KeyID,
7066
PublicKey,
7167
key_id,
7268
load_der_public_key,
73-
read_embedded,
7469
)
75-
from sigstore.errors import Error, MetadataError, TUFError, VerificationError
70+
from sigstore.errors import Error, MetadataError, VerificationError
7671

7772
# Versions supported by this client
7873
REKOR_VERSIONS = [1, 2]
@@ -562,123 +557,3 @@ def get_timestamp_authorities(self) -> list[CertificateAuthority]:
562557
for cert_chain in self._inner.timestamp_authorities
563558
]
564559
return certificate_authorities
565-
566-
567-
class ClientTrustConfig:
568-
"""
569-
Represents a Sigstore client's trust configuration, including a root of trust.
570-
"""
571-
572-
class ClientTrustConfigType(str, Enum):
573-
"""
574-
Known Sigstore client trust config media types.
575-
"""
576-
577-
CONFIG_0_1 = "application/vnd.dev.sigstore.clienttrustconfig.v0.1+json"
578-
579-
def __str__(self) -> str:
580-
"""Returns the variant's string value."""
581-
return self.value
582-
583-
@classmethod
584-
def from_json(cls, raw: str) -> ClientTrustConfig:
585-
"""
586-
Deserialize the given client trust config.
587-
"""
588-
inner = _ClientTrustConfig().from_json(raw)
589-
return cls(inner)
590-
591-
@classmethod
592-
def production(
593-
cls,
594-
offline: bool = False,
595-
) -> ClientTrustConfig:
596-
"""Create new trust config from Sigstore production TUF repository.
597-
598-
If `offline`, will use data in local TUF cache. Otherwise will
599-
update the data from remote TUF repository.
600-
"""
601-
return cls.from_tuf(DEFAULT_TUF_URL, offline)
602-
603-
@classmethod
604-
def staging(
605-
cls,
606-
offline: bool = False,
607-
) -> ClientTrustConfig:
608-
"""Create new trust config from Sigstore staging TUF repository.
609-
610-
If `offline`, will use data in local TUF cache. Otherwise will
611-
update the data from remote TUF repository.
612-
"""
613-
return cls.from_tuf(STAGING_TUF_URL, offline)
614-
615-
@classmethod
616-
def from_tuf(
617-
cls,
618-
url: str,
619-
offline: bool = False,
620-
) -> ClientTrustConfig:
621-
"""Create a new trust config from a TUF repository.
622-
623-
If `offline`, will use data in local TUF cache. Otherwise will
624-
update the trust config from remote TUF repository.
625-
"""
626-
updater = TrustUpdater(url, offline)
627-
628-
tr_path = updater.get_trusted_root_path()
629-
inner_tr = _TrustedRoot().from_json(Path(tr_path).read_bytes())
630-
631-
try:
632-
sc_path = updater.get_signing_config_path()
633-
inner_sc = _SigningConfig().from_json(Path(sc_path).read_bytes())
634-
except TUFError as e:
635-
# TUF repo may not have signing config yet: hard code values for prod:
636-
# https://github.com/sigstore/sigstore-python/issues/1388
637-
if url == DEFAULT_TUF_URL:
638-
embedded = read_embedded("signing_config.v0.2.json", url)
639-
inner_sc = _SigningConfig().from_json(embedded)
640-
else:
641-
raise e
642-
643-
return cls(
644-
_ClientTrustConfig(
645-
ClientTrustConfig.ClientTrustConfigType.CONFIG_0_1,
646-
inner_tr,
647-
inner_sc,
648-
)
649-
)
650-
651-
def __init__(self, inner: _ClientTrustConfig) -> None:
652-
"""
653-
@api private
654-
"""
655-
self._inner = inner
656-
self._verify()
657-
658-
def _verify(self) -> None:
659-
"""
660-
Performs various feats of heroism to ensure that the client trust config
661-
is well-formed.
662-
"""
663-
664-
# The client trust config must have a recognized media type.
665-
try:
666-
ClientTrustConfig.ClientTrustConfigType(self._inner.media_type)
667-
except ValueError:
668-
raise Error(
669-
f"unsupported client trust config format: {self._inner.media_type}"
670-
)
671-
672-
@property
673-
def trusted_root(self) -> TrustedRoot:
674-
"""
675-
Return the interior root of trust, as a `TrustedRoot`.
676-
"""
677-
return TrustedRoot(self._inner.trusted_root)
678-
679-
@property
680-
def signing_config(self) -> SigningConfig:
681-
"""
682-
Return the interior root of trust, as a `SigningConfig`.
683-
"""
684-
return SigningConfig(self._inner.signing_config)

sigstore/sign.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@
6161
from sigstore._internal.rekor import EntryRequestBody, RekorLogSubmitter
6262
from sigstore._internal.sct import verify_sct
6363
from sigstore._internal.timestamp import TimestampAuthorityClient, TimestampError
64-
from sigstore._internal.trust import ClientTrustConfig, KeyringPurpose, TrustedRoot
64+
from sigstore._internal.trust import KeyringPurpose, TrustedRoot
6565
from sigstore._utils import sha256_digest
6666
from sigstore.models import Bundle
6767
from sigstore.oidc import ExpiredIdentity, IdentityToken
68+
from sigstore.trust import ClientTrustConfig
6869

6970
_logger = logging.getLogger(__name__)
7071

sigstore/trust.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Copyright 2023 The Sigstore Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Client trust configuration for sigstore-python.
17+
"""
18+
19+
from __future__ import annotations
20+
21+
import logging
22+
from enum import Enum
23+
from pathlib import Path
24+
25+
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
26+
ClientTrustConfig as _ClientTrustConfig,
27+
)
28+
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
29+
SigningConfig as _SigningConfig,
30+
)
31+
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
32+
TrustedRoot as _TrustedRoot,
33+
)
34+
35+
from sigstore._internal.trust import SigningConfig, TrustedRoot
36+
from sigstore._internal.tuf import DEFAULT_TUF_URL, STAGING_TUF_URL, TrustUpdater
37+
from sigstore._utils import (
38+
read_embedded,
39+
)
40+
from sigstore.errors import Error, TUFError
41+
42+
_logger = logging.getLogger(__name__)
43+
44+
45+
class ClientTrustConfig:
46+
"""
47+
Represents a Sigstore client's trust configuration, including a root of trust.
48+
"""
49+
50+
class ClientTrustConfigType(str, Enum):
51+
"""
52+
Known Sigstore client trust config media types.
53+
"""
54+
55+
CONFIG_0_1 = "application/vnd.dev.sigstore.clienttrustconfig.v0.1+json"
56+
57+
def __str__(self) -> str:
58+
"""Returns the variant's string value."""
59+
return self.value
60+
61+
@classmethod
62+
def from_json(cls, raw: str) -> ClientTrustConfig:
63+
"""
64+
Deserialize the given client trust config.
65+
"""
66+
inner = _ClientTrustConfig().from_json(raw)
67+
return cls(inner)
68+
69+
@classmethod
70+
def production(
71+
cls,
72+
offline: bool = False,
73+
) -> ClientTrustConfig:
74+
"""Create new trust config from Sigstore production TUF repository.
75+
76+
If `offline`, will use data in local TUF cache. Otherwise will
77+
update the data from remote TUF repository.
78+
"""
79+
return cls.from_tuf(DEFAULT_TUF_URL, offline)
80+
81+
@classmethod
82+
def staging(
83+
cls,
84+
offline: bool = False,
85+
) -> ClientTrustConfig:
86+
"""Create new trust config from Sigstore staging TUF repository.
87+
88+
If `offline`, will use data in local TUF cache. Otherwise will
89+
update the data from remote TUF repository.
90+
"""
91+
return cls.from_tuf(STAGING_TUF_URL, offline)
92+
93+
@classmethod
94+
def from_tuf(
95+
cls,
96+
url: str,
97+
offline: bool = False,
98+
) -> ClientTrustConfig:
99+
"""Create a new trust config from a TUF repository.
100+
101+
If `offline`, will use data in local TUF cache. Otherwise will
102+
update the trust config from remote TUF repository.
103+
"""
104+
updater = TrustUpdater(url, offline)
105+
106+
tr_path = updater.get_trusted_root_path()
107+
inner_tr = _TrustedRoot().from_json(Path(tr_path).read_bytes())
108+
109+
try:
110+
sc_path = updater.get_signing_config_path()
111+
inner_sc = _SigningConfig().from_json(Path(sc_path).read_bytes())
112+
except TUFError as e:
113+
# TUF repo may not have signing config yet: hard code values for prod:
114+
# https://github.com/sigstore/sigstore-python/issues/1388
115+
if url == DEFAULT_TUF_URL:
116+
embedded = read_embedded("signing_config.v0.2.json", url)
117+
inner_sc = _SigningConfig().from_json(embedded)
118+
else:
119+
raise e
120+
121+
return cls(
122+
_ClientTrustConfig(
123+
ClientTrustConfig.ClientTrustConfigType.CONFIG_0_1,
124+
inner_tr,
125+
inner_sc,
126+
)
127+
)
128+
129+
def __init__(self, inner: _ClientTrustConfig) -> None:
130+
"""
131+
@api private
132+
"""
133+
self._inner = inner
134+
self._verify()
135+
136+
def _verify(self) -> None:
137+
"""
138+
Performs various feats of heroism to ensure that the client trust config
139+
is well-formed.
140+
"""
141+
142+
# The client trust config must have a recognized media type.
143+
try:
144+
ClientTrustConfig.ClientTrustConfigType(self._inner.media_type)
145+
except ValueError:
146+
raise Error(
147+
f"unsupported client trust config format: {self._inner.media_type}"
148+
)
149+
150+
@property
151+
def trusted_root(self) -> TrustedRoot:
152+
"""
153+
Return the interior root of trust, as a `TrustedRoot`.
154+
"""
155+
return TrustedRoot(self._inner.trusted_root)
156+
157+
@property
158+
def signing_config(self) -> SigningConfig:
159+
"""
160+
Return the interior root of trust, as a `SigningConfig`.
161+
"""
162+
return SigningConfig(self._inner.signing_config)

sigstore/verify/verifier.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@
4949
verify_sct,
5050
)
5151
from sigstore._internal.timestamp import TimestampSource, TimestampVerificationResult
52-
from sigstore._internal.trust import ClientTrustConfig, KeyringPurpose, TrustedRoot
52+
from sigstore._internal.trust import KeyringPurpose, TrustedRoot
5353
from sigstore._utils import base64_encode_pem_cert, sha256_digest
5454
from sigstore.errors import VerificationError
5555
from sigstore.hashes import Hashed
5656
from sigstore.models import Bundle
57+
from sigstore.trust import ClientTrustConfig
5758
from sigstore.verify.policy import VerificationPolicy
5859

5960
_logger = logging.getLogger(__name__)

test/unit/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@
3838
from sigstore._internal import tuf
3939
from sigstore._internal.rekor import _hashedrekord_from_parts
4040
from sigstore._internal.rekor.client import RekorClient
41-
from sigstore._internal.trust import ClientTrustConfig
4241
from sigstore._utils import sha256_digest
4342
from sigstore.models import Bundle
4443
from sigstore.oidc import IdentityToken
4544
from sigstore.sign import SigningContext
45+
from sigstore.trust import ClientTrustConfig
4646
from sigstore.verify.verifier import Verifier
4747

4848
_TUF_ASSETS = (Path(__file__).parent.parent / "assets" / "staging-tuf").resolve()

test/unit/internal/test_trust.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@
3232
from sigstore._internal.timestamp import TimestampAuthorityClient
3333
from sigstore._internal.trust import (
3434
CertificateAuthority,
35-
ClientTrustConfig,
3635
KeyringPurpose,
3736
SigningConfig,
3837
TrustedRoot,
3938
_is_timerange_valid,
4039
)
4140
from sigstore._utils import load_pem_public_key
4241
from sigstore.errors import Error
42+
from sigstore.trust import ClientTrustConfig
4343

4444
# Test data for TestSigningcconfig
4545
_service_v1_op1 = Service("url1", major_api_version=1, operator="op1")

0 commit comments

Comments
 (0)