Skip to content

Commit a13895d

Browse files
committed
feat: expose ClientTrustConfig as a public class
Signed-off-by: SequeI <[email protected]>
1 parent 204e0f4 commit a13895d

File tree

10 files changed

+130
-118
lines changed

10 files changed

+130
-118
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 public
69+
module (sigstore.models). This change formally adds the class to the project's public API,
70+
making it available for use in other projects. [#1496](https://github.com/sigstore/sigstore-python/pull/1496)
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

sigstore/_cli.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
from sigstore._internal.fulcio.client import ExpiredCertificate
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.dsse import StatementBuilder, Subject
4443
from sigstore.dsse._predicate import (
@@ -48,7 +47,7 @@
4847
)
4948
from sigstore.errors import Error, VerificationError
5049
from sigstore.hashes import Hashed
51-
from sigstore.models import Bundle, InvalidBundle
50+
from sigstore.models import Bundle, ClientTrustConfig, InvalidBundle
5251
from sigstore.oidc import (
5352
ExpiredIdentity,
5453
IdentityToken,

sigstore/_internal/rekor/client_v2.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import base64
2222
import json
2323
import logging
24+
import typing
2425

2526
import requests
2627
from cryptography.hazmat.primitives import serialization
@@ -38,7 +39,9 @@
3839
)
3940
from sigstore.dsse import Envelope
4041
from sigstore.hashes import Hashed
41-
from sigstore.models import TransparencyLogEntry
42+
43+
if typing.TYPE_CHECKING:
44+
from sigstore.models import TransparencyLogEntry
4245

4346
_logger = logging.getLogger(__name__)
4447

sigstore/_internal/trust.py

Lines changed: 9 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
from __future__ import annotations
2020

2121
import logging
22+
import typing
2223
from collections import defaultdict
2324
from collections.abc import Iterable
2425
from dataclasses import dataclass
2526
from datetime import datetime, timezone
2627
from enum import Enum
2728
from pathlib import Path
28-
from typing import ClassVar, NewType
2929

3030
import cryptography.hazmat.primitives.asymmetric.padding as padding
3131
from cryptography.exceptions import InvalidSignature
@@ -40,17 +40,18 @@
4040

4141
from sigstore._internal.fulcio.client import FulcioClient
4242
from sigstore._internal.rekor import RekorLogSubmitter
43-
from sigstore._internal.rekor.client import RekorClient
4443
from sigstore._internal.rekor.client_v2 import RekorV2Client
4544
from sigstore._internal.timestamp import TimestampAuthorityClient
46-
from sigstore._internal.tuf import DEFAULT_TUF_URL, STAGING_TUF_URL, TrustUpdater
4745
from sigstore._utils import (
4846
KeyID,
4947
PublicKey,
5048
key_id,
5149
load_der_public_key,
5250
)
53-
from sigstore.errors import Error, MetadataError, TUFError, VerificationError
51+
from sigstore.errors import Error, MetadataError, VerificationError
52+
53+
if typing.TYPE_CHECKING:
54+
from sigstore._internal.rekor.client import RekorClient
5455

5556
# Versions supported by this client
5657
REKOR_VERSIONS = [1, 2]
@@ -95,14 +96,14 @@ class Key:
9596
key: PublicKey
9697
key_id: KeyID
9798

98-
_RSA_SHA_256_DETAILS: ClassVar = {
99+
_RSA_SHA_256_DETAILS: typing.ClassVar = {
99100
common_v1.PublicKeyDetails.PKCS1_RSA_PKCS1V5,
100101
common_v1.PublicKeyDetails.PKIX_RSA_PKCS1V15_2048_SHA256,
101102
common_v1.PublicKeyDetails.PKIX_RSA_PKCS1V15_3072_SHA256,
102103
common_v1.PublicKeyDetails.PKIX_RSA_PKCS1V15_4096_SHA256,
103104
}
104105

105-
_EC_DETAILS_TO_HASH: ClassVar = {
106+
_EC_DETAILS_TO_HASH: typing.ClassVar = {
106107
common_v1.PublicKeyDetails.PKIX_ECDSA_P256_SHA_256: hashes.SHA256(),
107108
common_v1.PublicKeyDetails.PKIX_ECDSA_P384_SHA_384: hashes.SHA384(),
108109
common_v1.PublicKeyDetails.PKIX_ECDSA_P521_SHA_512: hashes.SHA512(),
@@ -221,8 +222,8 @@ def verify(self, *, key_id: KeyID, signature: bytes, data: bytes) -> None:
221222
raise VerificationError("keyring: invalid signature")
222223

223224

224-
RekorKeyring = NewType("RekorKeyring", Keyring)
225-
CTKeyring = NewType("CTKeyring", Keyring)
225+
RekorKeyring = typing.NewType("RekorKeyring", Keyring)
226+
CTKeyring = typing.NewType("CTKeyring", Keyring)
226227

227228

228229
class KeyringPurpose(str, Enum):
@@ -547,102 +548,3 @@ def get_timestamp_authorities(self) -> list[CertificateAuthority]:
547548
for cert_chain in self._inner.timestamp_authorities
548549
]
549550
return certificate_authorities
550-
551-
552-
class ClientTrustConfig:
553-
"""
554-
Represents a Sigstore client's trust configuration, including a root of trust.
555-
"""
556-
557-
class ClientTrustConfigType(str, Enum):
558-
"""
559-
Known Sigstore client trust config media types.
560-
"""
561-
562-
CONFIG_0_1 = "application/vnd.dev.sigstore.clienttrustconfig.v0.1+json"
563-
564-
def __str__(self) -> str:
565-
"""Returns the variant's string value."""
566-
return self.value
567-
568-
@classmethod
569-
def from_json(cls, raw: str) -> ClientTrustConfig:
570-
"""
571-
Deserialize the given client trust config.
572-
"""
573-
inner = trustroot_v1.ClientTrustConfig.from_json(raw)
574-
return cls(inner)
575-
576-
@classmethod
577-
def production(
578-
cls,
579-
offline: bool = False,
580-
) -> ClientTrustConfig:
581-
"""Create new trust config from Sigstore production TUF repository.
582-
583-
If `offline`, will use data in local TUF cache. Otherwise will
584-
update the data from remote TUF repository.
585-
"""
586-
return cls.from_tuf(DEFAULT_TUF_URL, offline)
587-
588-
@classmethod
589-
def staging(
590-
cls,
591-
offline: bool = False,
592-
) -> ClientTrustConfig:
593-
"""Create new trust config from Sigstore staging TUF repository.
594-
595-
If `offline`, will use data in local TUF cache. Otherwise will
596-
update the data from remote TUF repository.
597-
"""
598-
return cls.from_tuf(STAGING_TUF_URL, offline)
599-
600-
@classmethod
601-
def from_tuf(
602-
cls,
603-
url: str,
604-
offline: bool = False,
605-
) -> ClientTrustConfig:
606-
"""Create a new trust config from a TUF repository.
607-
608-
If `offline`, will use data in local TUF cache. Otherwise will
609-
update the trust config from remote TUF repository.
610-
"""
611-
updater = TrustUpdater(url, offline)
612-
613-
tr_path = updater.get_trusted_root_path()
614-
inner_tr = trustroot_v1.TrustedRoot.from_json(Path(tr_path).read_bytes())
615-
616-
try:
617-
sc_path = updater.get_signing_config_path()
618-
inner_sc = trustroot_v1.SigningConfig.from_json(Path(sc_path).read_bytes())
619-
except TUFError as e:
620-
raise e
621-
622-
return cls(
623-
trustroot_v1.ClientTrustConfig(
624-
media_type=ClientTrustConfig.ClientTrustConfigType.CONFIG_0_1.value,
625-
trusted_root=inner_tr,
626-
signing_config=inner_sc,
627-
)
628-
)
629-
630-
def __init__(self, inner: trustroot_v1.ClientTrustConfig) -> None:
631-
"""
632-
@api private
633-
"""
634-
self._inner = inner
635-
636-
@property
637-
def trusted_root(self) -> TrustedRoot:
638-
"""
639-
Return the interior root of trust, as a `TrustedRoot`.
640-
"""
641-
return TrustedRoot(self._inner.trusted_root)
642-
643-
@property
644-
def signing_config(self) -> SigningConfig:
645-
"""
646-
Return the interior root of trust, as a `SigningConfig`.
647-
"""
648-
return SigningConfig(self._inner.signing_config)

sigstore/models.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@
5858
if typing.TYPE_CHECKING:
5959
from sigstore._internal.trust import RekorKeyring
6060

61+
from pathlib import Path
62+
63+
from sigstore_models.trustroot import v1 as trustroot_v1
64+
65+
from sigstore._internal.trust import SigningConfig, TrustedRoot
66+
from sigstore._internal.tuf import DEFAULT_TUF_URL, STAGING_TUF_URL, TrustUpdater
67+
from sigstore.errors import TUFError
6168

6269
_logger = logging.getLogger(__name__)
6370

@@ -593,3 +600,102 @@ def _from_parts(
593600
)
594601

595602
return cls(inner)
603+
604+
605+
class ClientTrustConfig:
606+
"""
607+
Represents a Sigstore client's trust configuration, including a root of trust.
608+
"""
609+
610+
class ClientTrustConfigType(str, Enum):
611+
"""
612+
Known Sigstore client trust config media types.
613+
"""
614+
615+
CONFIG_0_1 = "application/vnd.dev.sigstore.clienttrustconfig.v0.1+json"
616+
617+
def __str__(self) -> str:
618+
"""Returns the variant's string value."""
619+
return self.value
620+
621+
@classmethod
622+
def from_json(cls, raw: str) -> ClientTrustConfig:
623+
"""
624+
Deserialize the given client trust config.
625+
"""
626+
inner = trustroot_v1.ClientTrustConfig.from_json(raw)
627+
return cls(inner)
628+
629+
@classmethod
630+
def production(
631+
cls,
632+
offline: bool = False,
633+
) -> ClientTrustConfig:
634+
"""Create new trust config from Sigstore production TUF repository.
635+
636+
If `offline`, will use data in local TUF cache. Otherwise will
637+
update the data from remote TUF repository.
638+
"""
639+
return cls.from_tuf(DEFAULT_TUF_URL, offline)
640+
641+
@classmethod
642+
def staging(
643+
cls,
644+
offline: bool = False,
645+
) -> ClientTrustConfig:
646+
"""Create new trust config from Sigstore staging TUF repository.
647+
648+
If `offline`, will use data in local TUF cache. Otherwise will
649+
update the data from remote TUF repository.
650+
"""
651+
return cls.from_tuf(STAGING_TUF_URL, offline)
652+
653+
@classmethod
654+
def from_tuf(
655+
cls,
656+
url: str,
657+
offline: bool = False,
658+
) -> ClientTrustConfig:
659+
"""Create a new trust config from a TUF repository.
660+
661+
If `offline`, will use data in local TUF cache. Otherwise will
662+
update the trust config from remote TUF repository.
663+
"""
664+
updater = TrustUpdater(url, offline)
665+
666+
tr_path = updater.get_trusted_root_path()
667+
inner_tr = trustroot_v1.TrustedRoot.from_json(Path(tr_path).read_bytes())
668+
669+
try:
670+
sc_path = updater.get_signing_config_path()
671+
inner_sc = trustroot_v1.SigningConfig.from_json(Path(sc_path).read_bytes())
672+
except TUFError as e:
673+
raise e
674+
675+
return cls(
676+
trustroot_v1.ClientTrustConfig(
677+
media_type=ClientTrustConfig.ClientTrustConfigType.CONFIG_0_1.value,
678+
trusted_root=inner_tr,
679+
signing_config=inner_sc,
680+
)
681+
)
682+
683+
def __init__(self, inner: trustroot_v1.ClientTrustConfig) -> None:
684+
"""
685+
@api private
686+
"""
687+
self._inner = inner
688+
689+
@property
690+
def trusted_root(self) -> TrustedRoot:
691+
"""
692+
Return the interior root of trust, as a `TrustedRoot`.
693+
"""
694+
return TrustedRoot(self._inner.trusted_root)
695+
696+
@property
697+
def signing_config(self) -> SigningConfig:
698+
"""
699+
Return the interior root of trust, as a `SigningConfig`.
700+
"""
701+
return SigningConfig(self._inner.signing_config)

sigstore/sign.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@
5959
from sigstore._internal.rekor import EntryRequestBody, RekorLogSubmitter
6060
from sigstore._internal.sct import verify_sct
6161
from sigstore._internal.timestamp import TimestampAuthorityClient, TimestampError
62-
from sigstore._internal.trust import ClientTrustConfig, KeyringPurpose, TrustedRoot
62+
from sigstore._internal.trust import KeyringPurpose, TrustedRoot
6363
from sigstore._utils import sha256_digest
64-
from sigstore.models import Bundle
64+
from sigstore.models import Bundle, ClientTrustConfig
6565
from sigstore.oidc import ExpiredIdentity, IdentityToken
6666

6767
_logger = logging.getLogger(__name__)

sigstore/verify/verifier.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@
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
56-
from sigstore.models import Bundle
56+
from sigstore.models import Bundle, ClientTrustConfig
5757
from sigstore.verify.policy import VerificationPolicy
5858

5959
_logger = logging.getLogger(__name__)

test/unit/conftest.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@
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
43-
from sigstore.models import Bundle
42+
from sigstore.models import Bundle, ClientTrustConfig
4443
from sigstore.oidc import IdentityToken
4544
from sigstore.sign import SigningContext
4645
from sigstore.verify.verifier import Verifier

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.models import ClientTrustConfig
4343

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

test/unit/test_sign.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121

2222
import sigstore.oidc
2323
from sigstore._internal.timestamp import TimestampAuthorityClient
24-
from sigstore._internal.trust import ClientTrustConfig
2524
from sigstore.dsse import StatementBuilder, Subject
2625
from sigstore.errors import VerificationError
2726
from sigstore.hashes import Hashed
27+
from sigstore.models import ClientTrustConfig
2828
from sigstore.sign import SigningContext
2929
from sigstore.verify.policy import UnsafeNoOp
3030

0 commit comments

Comments
 (0)