21
21
import logging
22
22
from typing import Any , Dict , List , Literal , Optional , Union
23
23
24
+ from cryptography .exceptions import InvalidSignature
24
25
from cryptography .hazmat .primitives import hashes
25
26
from cryptography .hazmat .primitives .asymmetric import ec
26
27
from pydantic import BaseModel , ConfigDict , Field , RootModel , StrictStr , ValidationError
27
28
from sigstore_protobuf_specs .io .intoto import Envelope as _Envelope
28
29
from sigstore_protobuf_specs .io .intoto import Signature
29
30
31
+ from sigstore .errors import VerificationError
32
+
30
33
_logger = logging .getLogger (__name__ )
31
34
32
35
_Digest = Union [
@@ -103,12 +106,7 @@ def _pae(self) -> bytes:
103
106
Construct the PAE encoding for this statement.
104
107
"""
105
108
106
- # See:
107
- # https://github.com/secure-systems-lab/dsse/blob/v1.0.0/envelope.md
108
- # https://github.com/in-toto/attestation/blob/v1.0/spec/v1.0/envelope.md
109
- pae = f"DSSEv1 { len (Envelope ._TYPE )} { Envelope ._TYPE } " .encode ()
110
- pae += b" " .join ([str (len (self ._contents )).encode (), self ._contents ])
111
- return pae
109
+ return _pae (Envelope ._TYPE , self ._contents )
112
110
113
111
114
112
class _StatementBuilder :
@@ -193,6 +191,19 @@ def to_json(self) -> str:
193
191
return self ._inner .to_json () # type: ignore[no-any-return]
194
192
195
193
194
+ def _pae (type_ : str , body : bytes ) -> bytes :
195
+ """
196
+ Compute the PAE encoding for the given `type_` and `body`.
197
+ """
198
+
199
+ # See:
200
+ # https://github.com/secure-systems-lab/dsse/blob/v1.0.0/envelope.md
201
+ # https://github.com/in-toto/attestation/blob/v1.0/spec/v1.0/envelope.md
202
+ pae = f"DSSEv1 { len (type_ )} { type_ } " .encode ()
203
+ pae += b" " .join ([str (len (body )).encode (), body ])
204
+ return pae
205
+
206
+
196
207
def _sign (key : ec .EllipticCurvePrivateKey , stmt : Statement ) -> Envelope :
197
208
"""
198
209
Sign for the given in-toto `Statement`, and encapsulate the resulting
@@ -209,3 +220,30 @@ def _sign(key: ec.EllipticCurvePrivateKey, stmt: Statement) -> Envelope:
209
220
signatures = [Signature (sig = signature , keyid = None )],
210
221
)
211
222
)
223
+
224
+
225
+ def _verify (key : ec .EllipticCurvePublicKey , evp : Envelope ) -> bytes :
226
+ """
227
+ Verify the given in-toto `Envelope`, returning the verified inner payload.
228
+
229
+ This function does **not** check the envelope's payload type. The caller
230
+ is responsible for performing this check.
231
+ """
232
+
233
+ pae = _pae (evp ._inner .payload_type , evp ._inner .payload )
234
+
235
+ if not evp ._inner .signatures :
236
+ raise VerificationError ("DSSE: envelope contains no signatures" )
237
+
238
+ # In practice checking more than one signature here is frivolous, since
239
+ # they're all being checked against the same key. But there's no
240
+ # particular harm in checking them all either.
241
+ for signature in evp ._inner .signatures :
242
+ try :
243
+ key .verify (signature .sig , pae , ec .ECDSA (hashes .SHA256 ()))
244
+ except InvalidSignature :
245
+ raise VerificationError ("DSSE: invalid signature" )
246
+
247
+ # TODO: Remove ignore when protobuf-specs contains a py.typed marker.
248
+ # See: <https://github.com/sigstore/protobuf-specs/pull/287>
249
+ return evp ._inner .payload # type: ignore[no-any-return]
0 commit comments