@@ -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