Skip to content

Commit 3ed46e5

Browse files
authored
Merge pull request #630 from secure-systems-lab/new-sigstore-api
Adapt to new sigstore api
2 parents bb3e894 + 856aa57 commit 3ed46e5

File tree

3 files changed

+52
-18
lines changed

3 files changed

+52
-18
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ awskms = ["boto3", "botocore", "cryptography>=40.0.0"]
5050
hsm = ["asn1crypto", "cryptography>=40.0.0", "PyKCS11"]
5151
pynacl = ["pynacl>1.2.0"]
5252
PySPX = ["PySPX>=0.5.0"]
53-
sigstore = ["sigstore==1.1.2"]
53+
sigstore = ["sigstore~=2.0"]
5454

5555
[tool.hatch.version]
5656
path = "securesystemslib/__init__.py"

requirements-sigstore.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sigstore==1.1.2
1+
sigstore==2.0.0

securesystemslib/signer/_sigstore_signer.py

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,10 @@ class SigstoreSigner(Signer):
130130

131131
SCHEME = "sigstore"
132132

133-
def __init__(self, token: str, public_key: Key):
134-
# TODO: Vet public key
135-
# - signer eligible for keytype/scheme?
136-
# - token matches identity/issuer?
133+
def __init__(self, token: Any, public_key: Key):
137134
self.public_key = public_key
135+
# token is of type sigstore.oidc.IdentityToken but the module should be usable
136+
# without sigstore so it's not annotated
138137
self._token = token
139138

140139
@classmethod
@@ -146,7 +145,7 @@ def from_priv_key_uri(
146145
) -> "SigstoreSigner":
147146
# pylint: disable=import-outside-toplevel
148147
try:
149-
from sigstore.oidc import Issuer, detect_credential
148+
from sigstore.oidc import IdentityToken, Issuer, detect_credential
150149
except ImportError as e:
151150
raise UnsupportedLibraryError(IMPORT_ERROR) from e
152151

@@ -159,16 +158,31 @@ def from_priv_key_uri(
159158
raise ValueError(f"SigstoreSigner does not support {priv_key_uri}")
160159

161160
params = dict(parse.parse_qsl(uri.query))
161+
ambient = params.get("ambient", "true") == "true"
162162

163-
if params.get("ambient") == "false":
163+
if not ambient:
164164
# TODO: Restrict oauth flow to use identity/issuer from public_key
165165
# TODO: Use secrets_handler for identity_token() secret arg
166-
issuer = Issuer.production()
167-
token = issuer.identity_token()
166+
token = Issuer.production().identity_token()
168167
else:
169-
# Note: this method signature only works with sigstore-python 1.1.2:
170-
# dependencies must be updated when changing this
171-
token = detect_credential("sigstore")
168+
credential = detect_credential()
169+
if not credential:
170+
raise RuntimeError("Failed to detect Sigstore credentials")
171+
token = IdentityToken(credential)
172+
173+
key_identity = public_key.keyval["identity"]
174+
key_issuer = public_key.keyval["issuer"]
175+
if key_issuer != token.expected_certificate_subject:
176+
raise ValueError(
177+
f"Signer identity issuer {token.expected_certificate_subject} "
178+
f"did not match key: {key_issuer}"
179+
)
180+
# TODO: should check ambient identity too: unfortunately IdentityToken does
181+
# not provide access to the expected identity value (cert SAN) in ambient case
182+
if not ambient and key_identity != token.identity:
183+
raise ValueError(
184+
f"Signer identity {token.identity} did not match key: {key_identity}"
185+
)
172186

173187
return cls(token, public_key)
174188

@@ -200,6 +214,25 @@ def import_(
200214

201215
return uri, key
202216

217+
@classmethod
218+
def import_via_auth(cls) -> Tuple[str, SigstoreKey]:
219+
"""Create public key and signer URI by interactive authentication
220+
221+
Returns a private key URI (for Signer.from_priv_key_uri()) and a public
222+
key. This method always uses the interactive authentication.
223+
"""
224+
# pylint: disable=import-outside-toplevel
225+
try:
226+
from sigstore.oidc import Issuer
227+
except ImportError as e:
228+
raise UnsupportedLibraryError(IMPORT_ERROR) from e
229+
230+
# authenticate to get the identity and issuer
231+
token = Issuer.production().identity_token()
232+
return cls.import_(
233+
token.identity, token.expected_certificate_subject, False
234+
)
235+
203236
def sign(self, payload: bytes) -> Signature:
204237
"""Signs payload using the OIDC token on the signer instance.
205238
@@ -217,14 +250,15 @@ def sign(self, payload: bytes) -> Signature:
217250
"""
218251
# pylint: disable=import-outside-toplevel
219252
try:
220-
from sigstore.sign import Signer as _Signer
253+
from sigstore.sign import SigningContext
221254
except ImportError as e:
222255
raise UnsupportedLibraryError(IMPORT_ERROR) from e
223256

224-
signer = _Signer.production()
225-
result = signer.sign(io.BytesIO(payload), self._token)
226-
# TODO: Ask upstream if they can make this public
227-
bundle = result._to_bundle() # pylint: disable=protected-access
257+
context = SigningContext.production()
258+
with context.signer(self._token) as sigstore_signer:
259+
result = sigstore_signer.sign(io.BytesIO(payload))
260+
261+
bundle = result.to_bundle()
228262

229263
return Signature(
230264
self.public_key.keyid,

0 commit comments

Comments
 (0)