Skip to content

Commit 669c49b

Browse files
committed
_internal/trust: Upgrade to SigningConfig 0.2
* Let's forget that v0.1 ever existed (it was not really used): We could try to support both but since 0.1 does not really work, I won't bother * Support signing config v0.2 in a minimal way (see note on selectors below) Things that could be improved: * Rekor client is still a bit of a hack: that area likely needs a redesign * The "service selectors" in SigningConfig are not all yet supported: Only the ANY selector works (this is the one staging will use soon) * The CLI does not yet use the OIDC provider specified in SigningConfig (this should be a small refactor) Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
1 parent 6b48b21 commit 669c49b

File tree

7 files changed

+318
-23
lines changed

7 files changed

+318
-23
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ dependencies = [
3838
"rfc8785 ~= 0.1.2",
3939
"rfc3161-client >= 0.1.2,< 1.1.0",
4040
# NOTE(ww): Both under active development, so strictly pinned.
41-
"sigstore-protobuf-specs == 0.3.5",
41+
"sigstore-protobuf-specs == 0.4.1",
4242
"sigstore-rekor-types == 0.0.18",
4343
"tuf ~= 6.0",
4444
"platformdirs ~= 4.2",

sigstore/_internal/trust.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,13 @@
4545
ClientTrustConfig as _ClientTrustConfig,
4646
)
4747
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
48+
Service,
49+
ServiceSelector,
4850
TransparencyLogInstance,
4951
)
52+
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
53+
SigningConfig as _SigningConfig,
54+
)
5055
from sigstore_protobuf_specs.dev.sigstore.trustroot.v1 import (
5156
TrustedRoot as _TrustedRoot,
5257
)
@@ -278,6 +283,114 @@ def certificates(self, *, allow_expired: bool) -> list[Certificate]:
278283
return self._certificates
279284

280285

286+
class SigningConfig:
287+
"""
288+
Signing configuration for a Sigstore instance.
289+
"""
290+
291+
class SigningConfigType(str, Enum):
292+
"""
293+
Known Sigstore signing config media types.
294+
"""
295+
296+
SIGNING_CONFIG_0_2 = "application/vnd.dev.sigstore.signingconfig.v0.2+json"
297+
298+
def __str__(self) -> str:
299+
"""Returns the variant's string value."""
300+
return self.value
301+
302+
def __init__(self, inner: _SigningConfig):
303+
"""
304+
Construct a new `SigningConfig`.
305+
306+
@api private
307+
"""
308+
self._inner = inner
309+
self._verify()
310+
311+
def _verify(self) -> None:
312+
"""
313+
Performs various feats of heroism to ensure that the signing config
314+
is well-formed.
315+
"""
316+
317+
# must have a recognized media type.
318+
try:
319+
SigningConfig.SigningConfigType(self._inner.media_type)
320+
except ValueError:
321+
raise Error(f"unsupported signing config format: {self._inner.media_type}")
322+
323+
# currently not supporting other select modes
324+
# TODO: Support other modes ensuring tsa_urls() and tlog_urls() work
325+
if self._inner.rekor_tlog_config.selector != ServiceSelector.ANY:
326+
raise Error(
327+
f"unsupported tlog selector {self._inner.rekor_tlog_config.selector}"
328+
)
329+
if self._inner.tsa_config.selector != ServiceSelector.ANY:
330+
raise Error(f"unsupported TSA selector {self._inner.tsa_config.selector}")
331+
332+
@classmethod
333+
def from_file(
334+
cls,
335+
path: str,
336+
) -> SigningConfig:
337+
"""Create a new signing config from file"""
338+
inner = _SigningConfig().from_json(Path(path).read_bytes())
339+
return cls(inner)
340+
341+
@staticmethod
342+
def _get_valid_service_url(services: list[Service]) -> str | None:
343+
for service in services:
344+
if service.major_api_version != 1:
345+
continue
346+
347+
if not _is_timerange_valid(service.valid_for, allow_expired=False):
348+
continue
349+
return service.url
350+
return None
351+
352+
def get_tlog_urls(self) -> list[str]:
353+
"""
354+
Returns the rekor transparency logs that client should sign with.
355+
Currently only returns a single one but could in future return several
356+
"""
357+
358+
url = self._get_valid_service_url(self._inner.rekor_tlog_urls)
359+
if not url:
360+
raise Error("No valid Rekor transparency log found in signing config")
361+
return [url]
362+
363+
def get_fulcio_url(self) -> str:
364+
"""
365+
Returns url for the fulcio instance that client should use to get a
366+
signing certificate from
367+
"""
368+
url = self._get_valid_service_url(self._inner.ca_urls)
369+
if not url:
370+
raise Error("No valid Fulcio CA found in signing config")
371+
return url
372+
373+
def get_oidc_url(self) -> str:
374+
"""
375+
Returns url for the OIDC provider that client should use to interactively
376+
authenticate.
377+
"""
378+
url = self._get_valid_service_url(self._inner.oidc_urls)
379+
if not url:
380+
raise Error("No valid OIDC provider found in signing config")
381+
return url
382+
383+
def get_tsa_urls(self) -> list[str]:
384+
"""
385+
Returns timestamp authority API end points. Currently returns a single one
386+
but may return more in future.
387+
"""
388+
url = self._get_valid_service_url(self._inner.tsa_urls)
389+
if not url:
390+
raise Error("No valid Timestamp Authority found in signing config")
391+
return [url]
392+
393+
281394
class TrustedRoot:
282395
"""
283396
The cryptographic root(s) of trust for a Sigstore instance.
@@ -473,3 +586,10 @@ def trusted_root(self) -> TrustedRoot:
473586
Return the interior root of trust, as a `TrustedRoot`.
474587
"""
475588
return TrustedRoot(self._inner.trusted_root)
589+
590+
@property
591+
def signing_config(self) -> SigningConfig:
592+
"""
593+
Return the interior root of trust, as a `SigningConfig`.
594+
"""
595+
return SigningConfig(self._inner.signing_config)

sigstore/sign.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,13 +352,13 @@ def _from_trust_config(cls, trust_config: ClientTrustConfig) -> SigningContext:
352352
353353
@api private
354354
"""
355+
signing_config = trust_config.signing_config
355356
return cls(
356-
fulcio=FulcioClient(trust_config._inner.signing_config.ca_url),
357-
rekor=RekorClient(trust_config._inner.signing_config.tlog_urls[0]),
357+
fulcio=FulcioClient(signing_config.get_fulcio_url()),
358+
rekor=RekorClient(signing_config.get_tlog_urls()[0]),
358359
trusted_root=trust_config.trusted_root,
359360
tsa_clients=[
360-
TimestampAuthorityClient(tsa_url)
361-
for tsa_url in trust_config._inner.signing_config.tsa_urls
361+
TimestampAuthorityClient(url) for url in signing_config.get_tsa_urls()
362362
],
363363
)
364364

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"mediaType": "application/vnd.dev.sigstore.signingconfig.v0.2+json",
3+
"caUrls": [
4+
{
5+
"url": "https://fulcio.example.com",
6+
"majorApiVersion": 1,
7+
"validFor": {
8+
"start": "2023-04-14T21:38:40Z"
9+
}
10+
},
11+
{
12+
"url": "https://fulcio-old.example.com",
13+
"majorApiVersion": 1,
14+
"validFor": {
15+
"start": "2022-04-14T21:38:40Z",
16+
"end": "2023-04-14T21:38:40Z"
17+
}
18+
}
19+
],
20+
"oidcUrls": [
21+
{
22+
"url": "https://oauth2.example.com/auth",
23+
"majorApiVersion": 1,
24+
"validFor": {
25+
"start": "2025-04-16T00:00:00Z"
26+
}
27+
}
28+
],
29+
"rekorTlogUrls": [
30+
{
31+
"url": "https://rekor.example.com",
32+
"majorApiVersion": 1,
33+
"validFor": {
34+
"start": "2021-01-12T11:53:27Z"
35+
}
36+
},
37+
{
38+
"url": "https://rekor-v2.example.com",
39+
"majorApiVersion": 2,
40+
"validFor": {
41+
"start": "2021-01-12T11:53:27Z"
42+
}
43+
}
44+
],
45+
"tsaUrls": [
46+
{
47+
"url": "https://timestamp.example.com/api/v1/timestamp",
48+
"majorApiVersion": 1,
49+
"validFor": {
50+
"start": "2025-04-09T00:00:00Z"
51+
}
52+
}
53+
],
54+
"rekorTlogConfig": {
55+
"selector": "ANY"
56+
},
57+
"tsaConfig": {
58+
"selector": "ANY"
59+
}
60+
}

test/assets/trust_config/config.badtype.json

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,64 @@
114114
}
115115
]
116116
},
117-
"signingConfig": {
118-
"caUrl": "https://fakeca.example.com",
119-
"oidcUrl": "https://fakeoidc.example.com",
120-
"tlogUrls": [
121-
"https://fakelog.example.com"
117+
"signing_config": {
118+
"mediaType": "application/vnd.dev.sigstore.signingconfig.v0.2+json",
119+
"caUrls": [
120+
{
121+
"url": "https://fulcio.example.com",
122+
"majorApiVersion": 1,
123+
"validFor": {
124+
"start": "2023-04-14T21:38:40Z"
125+
}
126+
},
127+
{
128+
"url": "https://fulcio-old.example.com",
129+
"majorApiVersion": 1,
130+
"validFor": {
131+
"start": "2022-04-14T21:38:40Z",
132+
"end": "2023-04-14T21:38:40Z"
133+
}
134+
}
135+
],
136+
"oidcUrls": [
137+
{
138+
"url": "https://oauth2.example.com/auth",
139+
"majorApiVersion": 1,
140+
"validFor": {
141+
"start": "2025-04-16T00:00:00Z"
142+
}
143+
}
144+
],
145+
"rekorTlogUrls": [
146+
{
147+
"url": "https://rekor.example.com",
148+
"majorApiVersion": 1,
149+
"validFor": {
150+
"start": "2021-01-12T11:53:27Z"
151+
}
152+
},
153+
{
154+
"url": "https://rekor-v2.example.com",
155+
"majorApiVersion": 2,
156+
"validFor": {
157+
"start": "2021-01-12T11:53:27Z"
158+
}
159+
}
122160
],
123161
"tsaUrls": [
124-
"https://faketsa.example.com"
125-
]
162+
{
163+
"url": "https://timestamp.example.com/api/v1/timestamp",
164+
"majorApiVersion": 1,
165+
"validFor": {
166+
"start": "2025-04-09T00:00:00Z"
167+
}
168+
}
169+
],
170+
"rekorTlogConfig": {
171+
"selector": "ANY"
172+
},
173+
"tsaConfig": {
174+
"selector": "ANY"
175+
}
126176
}
127177
}

test/assets/trust_config/config.v1.json

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,64 @@
114114
}
115115
]
116116
},
117-
"signingConfig": {
118-
"caUrl": "https://fakeca.example.com",
119-
"oidcUrl": "https://fakeoidc.example.com",
120-
"tlogUrls": [
121-
"https://fakelog.example.com"
117+
"signing_config": {
118+
"mediaType": "application/vnd.dev.sigstore.signingconfig.v0.2+json",
119+
"caUrls": [
120+
{
121+
"url": "https://fulcio.example.com",
122+
"majorApiVersion": 1,
123+
"validFor": {
124+
"start": "2023-04-14T21:38:40Z"
125+
}
126+
},
127+
{
128+
"url": "https://fulcio-old.example.com",
129+
"majorApiVersion": 1,
130+
"validFor": {
131+
"start": "2022-04-14T21:38:40Z",
132+
"end": "2023-04-14T21:38:40Z"
133+
}
134+
}
135+
],
136+
"oidcUrls": [
137+
{
138+
"url": "https://oauth2.example.com/auth",
139+
"majorApiVersion": 1,
140+
"validFor": {
141+
"start": "2025-04-16T00:00:00Z"
142+
}
143+
}
144+
],
145+
"rekorTlogUrls": [
146+
{
147+
"url": "https://rekor.example.com",
148+
"majorApiVersion": 1,
149+
"validFor": {
150+
"start": "2021-01-12T11:53:27Z"
151+
}
152+
},
153+
{
154+
"url": "https://rekor-v2.example.com",
155+
"majorApiVersion": 2,
156+
"validFor": {
157+
"start": "2021-01-12T11:53:27Z"
158+
}
159+
}
122160
],
123161
"tsaUrls": [
124-
"https://faketsa.example.com"
125-
]
162+
{
163+
"url": "https://timestamp.example.com/api/v1/timestamp",
164+
"majorApiVersion": 1,
165+
"validFor": {
166+
"start": "2025-04-09T00:00:00Z"
167+
}
168+
}
169+
],
170+
"rekorTlogConfig": {
171+
"selector": "ANY"
172+
},
173+
"tsaConfig": {
174+
"selector": "ANY"
175+
}
126176
}
127177
}

0 commit comments

Comments
 (0)