Edit on GitHub

model_signing.verifying

High level API for the verification interface of model_signing library.

This module supports configuring the verification method used to verify a model, before performing the verification.

model_signing.verifying.Config().use_sigstore_verifier(
    identity=identity, oidc_issuer=oidc_provider
).verify("finbert", "finbert.sig")

The same verification configuration can be used to verify multiple models:

verifying_config = model_signing.signing.Config().use_elliptic_key_verifier(
    public_key="key.pub"
)

for model in all_models:
    verifying_config.verify(model, f"{model}_sharded.sig")

The API defined here is stable and backwards compatible.

  1# Copyright 2024 The Sigstore Authors
  2#
  3# Licensed under the Apache License, Version 2.0 (the "License");
  4# you may not use this file except in compliance with the License.
  5# You may obtain a copy of the License at
  6#
  7#      http://www.apache.org/licenses/LICENSE-2.0
  8#
  9# Unless required by applicable law or agreed to in writing, software
 10# distributed under the License is distributed on an "AS IS" BASIS,
 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12# See the License for the specific language governing permissions and
 13# limitations under the License.
 14
 15"""High level API for the verification interface of `model_signing` library.
 16
 17This module supports configuring the verification method used to verify a model,
 18before performing the verification.
 19
 20```python
 21model_signing.verifying.Config().use_sigstore_verifier(
 22    identity=identity, oidc_issuer=oidc_provider
 23).verify("finbert", "finbert.sig")
 24```
 25
 26The same verification configuration can be used to verify multiple models:
 27
 28```python
 29verifying_config = model_signing.signing.Config().use_elliptic_key_verifier(
 30    public_key="key.pub"
 31)
 32
 33for model in all_models:
 34    verifying_config.verify(model, f"{model}_sharded.sig")
 35```
 36
 37The API defined here is stable and backwards compatible.
 38"""
 39
 40from collections.abc import Iterable
 41import pathlib
 42import sys
 43
 44from model_signing import hashing
 45from model_signing import manifest
 46from model_signing._signing import sign_certificate as certificate
 47from model_signing._signing import sign_ec_key as ec_key
 48from model_signing._signing import sign_sigstore as sigstore
 49from model_signing._signing import sign_sigstore_pb as sigstore_pb
 50
 51
 52if sys.version_info >= (3, 11):
 53    from typing import Self
 54else:
 55    from typing_extensions import Self
 56
 57
 58class Config:
 59    """Configuration to use when verifying models against signatures.
 60
 61    The verification configuration is needed to determine how to read and verify
 62    the signature. Given we support multiple signing format, the verification
 63    settings must match the signing ones.
 64
 65    The configuration also supports configuring the hashing configuration from
 66    `model_signing.hashing`. This should also match the configuration used
 67    during signing. However, by default, we can attempt to guess it from the
 68    signature.
 69    """
 70
 71    def __init__(self):
 72        """Initializes the default configuration for verification."""
 73        self._hashing_config = None
 74        self._verifier = None
 75        self._uses_sigstore = False
 76
 77    def verify(
 78        self, model_path: hashing.PathLike, signature_path: hashing.PathLike
 79    ):
 80        """Verifies that a model conforms to a signature.
 81
 82        Args:
 83            model_path: The path to the model to verify.
 84
 85        Raises:
 86            ValueError: No verifier has been configured.
 87        """
 88        if self._verifier is None:
 89            raise ValueError("Attempting to verify with no configured verifier")
 90
 91        if self._uses_sigstore:
 92            signature = sigstore.Signature.read(pathlib.Path(signature_path))
 93        else:
 94            signature = sigstore_pb.Signature.read(pathlib.Path(signature_path))
 95
 96        expected_manifest = self._verifier.verify(signature)
 97
 98        if self._hashing_config is None:
 99            self._guess_hashing_config(expected_manifest)
100        actual_manifest = self._hashing_config.hash(model_path)
101
102        if actual_manifest != expected_manifest:
103            raise ValueError("Signature mismatch")
104
105    def set_hashing_config(self, hashing_config: hashing.Config) -> Self:
106        """Sets the new configuration for hashing models.
107
108        After calling this method, the automatic guessing of the hashing
109        configuration used during signing is no longer possible from within one
110        instance of this class.
111
112        Args:
113            hashing_config: The new hashing configuration.
114
115        Returns:
116            The new signing configuration.
117        """
118        self._hashing_config = hashing_config
119        return self
120
121    def _guess_hashing_config(self, source_manifest: manifest.Manifest) -> None:
122        """Attempts to guess the hashing config from a manifest."""
123        args = source_manifest.serialization_type
124        method = args["method"]
125        # TODO: Once Python 3.9 support is deprecated revert to using `match`
126        if method == "files":
127            self._hashing_config = hashing.Config().use_file_serialization(
128                hashing_algorithm=args["hash_type"],
129                allow_symlinks=args["allow_symlinks"],
130            )
131        elif method == "shards":
132            self._hashing_config = hashing.Config().use_shard_serialization(
133                hashing_algorithm=args["hash_type"],
134                shard_size=args["shard_size"],
135                allow_symlinks=args["allow_symlinks"],
136            )
137        else:
138            raise ValueError("Cannot guess the hashing configuration")
139
140    def use_sigstore_verifier(
141        self, *, identity: str, oidc_issuer: str, use_staging: bool = False
142    ) -> Self:
143        """Configures the verification of signatures produced by Sigstore.
144
145        The verifier in this configuration is changed to one that performs
146        verification of Sigstore signatures (sigstore bundles signed by
147        keyless signing via Sigstore).
148
149        Args:
150            identity: The expected identity that has signed the model.
151            oidc_issuer: The expected OpenID Connect issuer that provided the
152              certificate used for the signature.
153            use_staging: Use staging configurations, instead of production. This
154              is supposed to be set to True only when testing. Default is False.
155
156        Return:
157            The new verification configuration.
158        """
159        self._uses_sigstore = True
160        self._verifier = sigstore.Verifier(
161            identity=identity, oidc_issuer=oidc_issuer, use_staging=use_staging
162        )
163        return self
164
165    def use_elliptic_key_verifier(
166        self, *, public_key: hashing.PathLike
167    ) -> Self:
168        """Configures the verification of signatures generated by a private key.
169
170        The verifier in this configuration is changed to one that performs
171        verification of sgistore bundles signed by an elliptic curve private
172        key. The public key used in the configuration must match the private key
173        used during signing.
174
175        Args:
176            public_key: The path to the public key to verify with.
177
178        Return:
179            The new verification configuration.
180        """
181        self._uses_sigstore = False
182        self._verifier = ec_key.Verifier(pathlib.Path(public_key))
183        return self
184
185    def use_certificate_verifier(
186        self,
187        *,
188        certificate_chain: Iterable[hashing.PathLike] = frozenset(),
189        log_fingerprints: bool = False,
190    ) -> Self:
191        """Configures the verification of signatures generated by a certificate.
192
193        The verifier in this configuration is changed to one that performs
194        verification of sgistore bundles signed by a signing certificate.
195
196        Args:
197            certificate_chain: Certificate chain to establish root of trust. If
198              empty, the operating system's one is used.
199            log_fingerprints: Log certificates' SHA256 fingerprints
200
201        Return:
202            The new verification configuration.
203        """
204        self._uses_sigstore = False
205        self._verifier = certificate.Verifier(
206            [pathlib.Path(c) for c in certificate_chain],
207            log_fingerprints=log_fingerprints,
208        )
209        return self
class Config:
 59class Config:
 60    """Configuration to use when verifying models against signatures.
 61
 62    The verification configuration is needed to determine how to read and verify
 63    the signature. Given we support multiple signing format, the verification
 64    settings must match the signing ones.
 65
 66    The configuration also supports configuring the hashing configuration from
 67    `model_signing.hashing`. This should also match the configuration used
 68    during signing. However, by default, we can attempt to guess it from the
 69    signature.
 70    """
 71
 72    def __init__(self):
 73        """Initializes the default configuration for verification."""
 74        self._hashing_config = None
 75        self._verifier = None
 76        self._uses_sigstore = False
 77
 78    def verify(
 79        self, model_path: hashing.PathLike, signature_path: hashing.PathLike
 80    ):
 81        """Verifies that a model conforms to a signature.
 82
 83        Args:
 84            model_path: The path to the model to verify.
 85
 86        Raises:
 87            ValueError: No verifier has been configured.
 88        """
 89        if self._verifier is None:
 90            raise ValueError("Attempting to verify with no configured verifier")
 91
 92        if self._uses_sigstore:
 93            signature = sigstore.Signature.read(pathlib.Path(signature_path))
 94        else:
 95            signature = sigstore_pb.Signature.read(pathlib.Path(signature_path))
 96
 97        expected_manifest = self._verifier.verify(signature)
 98
 99        if self._hashing_config is None:
100            self._guess_hashing_config(expected_manifest)
101        actual_manifest = self._hashing_config.hash(model_path)
102
103        if actual_manifest != expected_manifest:
104            raise ValueError("Signature mismatch")
105
106    def set_hashing_config(self, hashing_config: hashing.Config) -> Self:
107        """Sets the new configuration for hashing models.
108
109        After calling this method, the automatic guessing of the hashing
110        configuration used during signing is no longer possible from within one
111        instance of this class.
112
113        Args:
114            hashing_config: The new hashing configuration.
115
116        Returns:
117            The new signing configuration.
118        """
119        self._hashing_config = hashing_config
120        return self
121
122    def _guess_hashing_config(self, source_manifest: manifest.Manifest) -> None:
123        """Attempts to guess the hashing config from a manifest."""
124        args = source_manifest.serialization_type
125        method = args["method"]
126        # TODO: Once Python 3.9 support is deprecated revert to using `match`
127        if method == "files":
128            self._hashing_config = hashing.Config().use_file_serialization(
129                hashing_algorithm=args["hash_type"],
130                allow_symlinks=args["allow_symlinks"],
131            )
132        elif method == "shards":
133            self._hashing_config = hashing.Config().use_shard_serialization(
134                hashing_algorithm=args["hash_type"],
135                shard_size=args["shard_size"],
136                allow_symlinks=args["allow_symlinks"],
137            )
138        else:
139            raise ValueError("Cannot guess the hashing configuration")
140
141    def use_sigstore_verifier(
142        self, *, identity: str, oidc_issuer: str, use_staging: bool = False
143    ) -> Self:
144        """Configures the verification of signatures produced by Sigstore.
145
146        The verifier in this configuration is changed to one that performs
147        verification of Sigstore signatures (sigstore bundles signed by
148        keyless signing via Sigstore).
149
150        Args:
151            identity: The expected identity that has signed the model.
152            oidc_issuer: The expected OpenID Connect issuer that provided the
153              certificate used for the signature.
154            use_staging: Use staging configurations, instead of production. This
155              is supposed to be set to True only when testing. Default is False.
156
157        Return:
158            The new verification configuration.
159        """
160        self._uses_sigstore = True
161        self._verifier = sigstore.Verifier(
162            identity=identity, oidc_issuer=oidc_issuer, use_staging=use_staging
163        )
164        return self
165
166    def use_elliptic_key_verifier(
167        self, *, public_key: hashing.PathLike
168    ) -> Self:
169        """Configures the verification of signatures generated by a private key.
170
171        The verifier in this configuration is changed to one that performs
172        verification of sgistore bundles signed by an elliptic curve private
173        key. The public key used in the configuration must match the private key
174        used during signing.
175
176        Args:
177            public_key: The path to the public key to verify with.
178
179        Return:
180            The new verification configuration.
181        """
182        self._uses_sigstore = False
183        self._verifier = ec_key.Verifier(pathlib.Path(public_key))
184        return self
185
186    def use_certificate_verifier(
187        self,
188        *,
189        certificate_chain: Iterable[hashing.PathLike] = frozenset(),
190        log_fingerprints: bool = False,
191    ) -> Self:
192        """Configures the verification of signatures generated by a certificate.
193
194        The verifier in this configuration is changed to one that performs
195        verification of sgistore bundles signed by a signing certificate.
196
197        Args:
198            certificate_chain: Certificate chain to establish root of trust. If
199              empty, the operating system's one is used.
200            log_fingerprints: Log certificates' SHA256 fingerprints
201
202        Return:
203            The new verification configuration.
204        """
205        self._uses_sigstore = False
206        self._verifier = certificate.Verifier(
207            [pathlib.Path(c) for c in certificate_chain],
208            log_fingerprints=log_fingerprints,
209        )
210        return self

Configuration to use when verifying models against signatures.

The verification configuration is needed to determine how to read and verify the signature. Given we support multiple signing format, the verification settings must match the signing ones.

The configuration also supports configuring the hashing configuration from model_signing.hashing. This should also match the configuration used during signing. However, by default, we can attempt to guess it from the signature.

Config()
72    def __init__(self):
73        """Initializes the default configuration for verification."""
74        self._hashing_config = None
75        self._verifier = None
76        self._uses_sigstore = False

Initializes the default configuration for verification.

def verify( self, model_path: Union[str, bytes, os.PathLike], signature_path: Union[str, bytes, os.PathLike]):
 78    def verify(
 79        self, model_path: hashing.PathLike, signature_path: hashing.PathLike
 80    ):
 81        """Verifies that a model conforms to a signature.
 82
 83        Args:
 84            model_path: The path to the model to verify.
 85
 86        Raises:
 87            ValueError: No verifier has been configured.
 88        """
 89        if self._verifier is None:
 90            raise ValueError("Attempting to verify with no configured verifier")
 91
 92        if self._uses_sigstore:
 93            signature = sigstore.Signature.read(pathlib.Path(signature_path))
 94        else:
 95            signature = sigstore_pb.Signature.read(pathlib.Path(signature_path))
 96
 97        expected_manifest = self._verifier.verify(signature)
 98
 99        if self._hashing_config is None:
100            self._guess_hashing_config(expected_manifest)
101        actual_manifest = self._hashing_config.hash(model_path)
102
103        if actual_manifest != expected_manifest:
104            raise ValueError("Signature mismatch")

Verifies that a model conforms to a signature.

Arguments:
  • model_path: The path to the model to verify.
Raises:
  • ValueError: No verifier has been configured.
def set_hashing_config(self, hashing_config: model_signing.hashing.Config) -> Self:
106    def set_hashing_config(self, hashing_config: hashing.Config) -> Self:
107        """Sets the new configuration for hashing models.
108
109        After calling this method, the automatic guessing of the hashing
110        configuration used during signing is no longer possible from within one
111        instance of this class.
112
113        Args:
114            hashing_config: The new hashing configuration.
115
116        Returns:
117            The new signing configuration.
118        """
119        self._hashing_config = hashing_config
120        return self

Sets the new configuration for hashing models.

After calling this method, the automatic guessing of the hashing configuration used during signing is no longer possible from within one instance of this class.

Arguments:
  • hashing_config: The new hashing configuration.
Returns:

The new signing configuration.

def use_sigstore_verifier( self, *, identity: str, oidc_issuer: str, use_staging: bool = False) -> Self:
141    def use_sigstore_verifier(
142        self, *, identity: str, oidc_issuer: str, use_staging: bool = False
143    ) -> Self:
144        """Configures the verification of signatures produced by Sigstore.
145
146        The verifier in this configuration is changed to one that performs
147        verification of Sigstore signatures (sigstore bundles signed by
148        keyless signing via Sigstore).
149
150        Args:
151            identity: The expected identity that has signed the model.
152            oidc_issuer: The expected OpenID Connect issuer that provided the
153              certificate used for the signature.
154            use_staging: Use staging configurations, instead of production. This
155              is supposed to be set to True only when testing. Default is False.
156
157        Return:
158            The new verification configuration.
159        """
160        self._uses_sigstore = True
161        self._verifier = sigstore.Verifier(
162            identity=identity, oidc_issuer=oidc_issuer, use_staging=use_staging
163        )
164        return self

Configures the verification of signatures produced by Sigstore.

The verifier in this configuration is changed to one that performs verification of Sigstore signatures (sigstore bundles signed by keyless signing via Sigstore).

Arguments:
  • identity: The expected identity that has signed the model.
  • oidc_issuer: The expected OpenID Connect issuer that provided the certificate used for the signature.
  • use_staging: Use staging configurations, instead of production. This is supposed to be set to True only when testing. Default is False.
Return:

The new verification configuration.

def use_elliptic_key_verifier(self, *, public_key: Union[str, bytes, os.PathLike]) -> Self:
166    def use_elliptic_key_verifier(
167        self, *, public_key: hashing.PathLike
168    ) -> Self:
169        """Configures the verification of signatures generated by a private key.
170
171        The verifier in this configuration is changed to one that performs
172        verification of sgistore bundles signed by an elliptic curve private
173        key. The public key used in the configuration must match the private key
174        used during signing.
175
176        Args:
177            public_key: The path to the public key to verify with.
178
179        Return:
180            The new verification configuration.
181        """
182        self._uses_sigstore = False
183        self._verifier = ec_key.Verifier(pathlib.Path(public_key))
184        return self

Configures the verification of signatures generated by a private key.

The verifier in this configuration is changed to one that performs verification of sgistore bundles signed by an elliptic curve private key. The public key used in the configuration must match the private key used during signing.

Arguments:
  • public_key: The path to the public key to verify with.
Return:

The new verification configuration.

def use_certificate_verifier( self, *, certificate_chain: Iterable[typing.Union[str, bytes, os.PathLike]] = frozenset(), log_fingerprints: bool = False) -> Self:
186    def use_certificate_verifier(
187        self,
188        *,
189        certificate_chain: Iterable[hashing.PathLike] = frozenset(),
190        log_fingerprints: bool = False,
191    ) -> Self:
192        """Configures the verification of signatures generated by a certificate.
193
194        The verifier in this configuration is changed to one that performs
195        verification of sgistore bundles signed by a signing certificate.
196
197        Args:
198            certificate_chain: Certificate chain to establish root of trust. If
199              empty, the operating system's one is used.
200            log_fingerprints: Log certificates' SHA256 fingerprints
201
202        Return:
203            The new verification configuration.
204        """
205        self._uses_sigstore = False
206        self._verifier = certificate.Verifier(
207            [pathlib.Path(c) for c in certificate_chain],
208            log_fingerprints=log_fingerprints,
209        )
210        return self

Configures the verification of signatures generated by a certificate.

The verifier in this configuration is changed to one that performs verification of sgistore bundles signed by a signing certificate.

Arguments:
  • certificate_chain: Certificate chain to establish root of trust. If empty, the operating system's one is used.
  • log_fingerprints: Log certificates' SHA256 fingerprints
Return:

The new verification configuration.