Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/model_signing/signing.py: 66%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

53 statements  

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 signing interface of `model_signing` library. 

16 

17The module allows signing a model with a default configuration: 

18 

19```python 

20model_signing.signing.sign("finbert", "finbert.sig") 

21``` 

22 

23The module allows customizing the signing configuration before signing: 

24 

25```python 

26model_signing.signing.Config().use_elliptic_key_signer(private_key="key").sign( 

27 "finbert", "finbert.sig" 

28) 

29``` 

30 

31The same signing configuration can be used to sign multiple models: 

32 

33```python 

34signing_config = model_signing.signing.Config().use_elliptic_key_signer( 

35 private_key="key" 

36) 

37 

38for model in all_models: 

39 signing_config.sign(model, f"{model}_sharded.sig") 

40``` 

41 

42The API defined here is stable and backwards compatible. 

43""" 

44 

45from collections.abc import Iterable 

46import pathlib 

47import sys 

48from typing import Optional 

49 

50from model_signing import hashing 

51from model_signing._signing import sign_certificate as certificate 

52from model_signing._signing import sign_ec_key as ec_key 

53from model_signing._signing import sign_sigstore as sigstore 

54from model_signing._signing import signing 

55 

56 

57if sys.version_info >= (3, 11): 

58 from typing import Self 

59else: 

60 from typing_extensions import Self 

61 

62 

63def sign(model_path: hashing.PathLike, signature_path: hashing.PathLike): 

64 """Signs a model using the default configuration. 

65 

66 In this default configuration we sign using Sigstore and the default hashing 

67 configuration from `model_signing.hashing`. 

68 

69 The resulting signature is in the Sigstore bundle format. 

70 

71 Args: 

72 model_path: the path to the model to sign. 

73 signature_path: the path of the resulting signature. 

74 """ 

75 Config().sign(model_path, signature_path) 

76 

77 

78class Config: 

79 """Configuration to use when signing models. 

80 

81 Currently, we support signing with Sigstore (both the public 

82 instance and staging instance), signing with private keys, 

83 signing with signing certificates, and signing with custom 

84 PKI configurations using the `--trust_config` option. 

85 This allows users to bring their own trust configuration 

86 to sign and verify models. Other signing modes may be 

87 added in the future. 

88 """ 

89 

90 def __init__(self): 

91 """Initializes the default configuration for signing.""" 

92 self._hashing_config = hashing.Config() 

93 # lazy initialize default signer at signing to avoid network calls 

94 self._signer = None 

95 

96 def sign( 

97 self, model_path: hashing.PathLike, signature_path: hashing.PathLike 

98 ): 

99 """Signs a model using the current configuration. 

100 

101 Args: 

102 model_path: The path to the model to sign. 

103 signature_path: The path of the resulting signature. 

104 """ 

105 if not self._signer: 

106 self._signer = self.use_sigstore_signer() 

107 manifest = self._hashing_config.hash(model_path) 

108 payload = signing.Payload(manifest) 

109 signature = self._signer.sign(payload) 

110 signature.write(pathlib.Path(signature_path)) 

111 

112 def set_hashing_config(self, hashing_config: hashing.Config) -> Self: 

113 """Sets the new configuration for hashing models. 

114 

115 Args: 

116 hashing_config: The new hashing configuration. 

117 

118 Returns: 

119 The new signing configuration. 

120 """ 

121 self._hashing_config = hashing_config 

122 return self 

123 

124 def use_sigstore_signer( 

125 self, 

126 *, 

127 oidc_issuer: Optional[str] = None, 

128 use_ambient_credentials: bool = False, 

129 use_staging: bool = False, 

130 force_oob: bool = False, 

131 identity_token: Optional[str] = None, 

132 client_id: Optional[str] = None, 

133 client_secret: Optional[str] = None, 

134 trust_config: Optional[pathlib.Path] = None, 

135 ) -> Self: 

136 """Configures the signing to be performed with Sigstore. 

137 

138 The signer in this configuration is changed to one that performs signing 

139 with Sigstore. 

140 

141 Args: 

142 oidc_issuer: An optional OpenID Connect issuer to use instead of the 

143 default production one. Only relevant if `use_staging = False`. 

144 Default is empty, relying on the Sigstore configuration. 

145 use_ambient_credentials: Use ambient credentials (also known as 

146 Workload Identity). Default is False. If ambient credentials 

147 cannot be used (not available, or option disabled), a flow to get 

148 signer identity via OIDC will start. 

149 use_staging: Use staging configurations, instead of production. This 

150 is supposed to be set to True only when testing. Default is False. 

151 force_oob: If True, forces an out-of-band (OOB) OAuth flow. If set, 

152 the OAuth authentication will not attempt to open the default web 

153 browser. Instead, it will display a URL and code for manual 

154 authentication. Default is False, which means the browser will be 

155 opened automatically if possible. 

156 identity_token: An explicit identity token to use when signing, 

157 taking precedence over any ambient credential or OAuth workflow. 

158 client_id: An optional client ID to use when performing OIDC-based 

159 authentication. This is typically used to identify the 

160 application making the request to the OIDC provider. If not 

161 provided, the default client ID configured by Sigstore will be 

162 used. 

163 client_secret: An optional client secret to use along with the 

164 client ID when authenticating with the OIDC provider. This is 

165 required for confidential clients that need to prove their 

166 identity to the OIDC provider. If not provided, it is assumed 

167 that the client is public or the provider does not require a 

168 secret. 

169 trust_config: A path to a custom trust configuration. When provided, 

170 the signature verification process will rely on the supplied 

171 PKI and trust configurations, instead of the default Sigstore 

172 setup. If not specified, the default Sigstore configuration 

173 is used. 

174 

175 Return: 

176 The new signing configuration. 

177 """ 

178 self._signer = sigstore.Signer( 

179 oidc_issuer=oidc_issuer, 

180 use_ambient_credentials=use_ambient_credentials, 

181 use_staging=use_staging, 

182 identity_token=identity_token, 

183 force_oob=force_oob, 

184 client_id=client_id, 

185 client_secret=client_secret, 

186 trust_config=trust_config, 

187 ) 

188 return self 

189 

190 def use_elliptic_key_signer( 

191 self, *, private_key: hashing.PathLike, password: Optional[str] = None 

192 ) -> Self: 

193 """Configures the signing to be performed using elliptic curve keys. 

194 

195 The signer in this configuration is changed to one that performs signing 

196 using a private key based on elliptic curve cryptography. 

197 

198 Args: 

199 private_key: The path to the private key to use for signing. 

200 password: An optional password for the key, if encrypted. 

201 

202 Return: 

203 The new signing configuration. 

204 """ 

205 self._signer = ec_key.Signer(pathlib.Path(private_key), password) 

206 return self 

207 

208 def use_certificate_signer( 

209 self, 

210 *, 

211 private_key: hashing.PathLike, 

212 signing_certificate: hashing.PathLike, 

213 certificate_chain: Iterable[hashing.PathLike], 

214 ) -> Self: 

215 """Configures the signing to be performed using signing certificates. 

216 

217 The signer in this configuration is changed to one that performs signing 

218 using cryptographic signing certificates. 

219 

220 Args: 

221 private_key: The path to the private key to use for signing. 

222 signing_certificate: The path to the signing certificate. 

223 certificate_chain: Optional paths to other certificates to establish 

224 a chain of trust. 

225 

226 Return: 

227 The new signing configuration. 

228 """ 

229 self._signer = certificate.Signer( 

230 pathlib.Path(private_key), 

231 pathlib.Path(signing_certificate), 

232 [pathlib.Path(c) for c in certificate_chain], 

233 ) 

234 return self 

235 

236 def use_pkcs11_signer( 

237 self, *, pkcs11_uri: str, module_paths: Iterable[str] = frozenset() 

238 ) -> Self: 

239 """Configures the signing to be performed using PKCS #11. 

240 

241 The signer in this configuration is changed to one that performs signing 

242 using a private key based on elliptic curve cryptography. 

243 

244 Args: 

245 pkcs11_uri: The PKCS11 URI. 

246 module_paths: Optional list of paths of PKCS #11 modules. 

247 

248 Return: 

249 The new signing configuration. 

250 """ 

251 try: 

252 from model_signing._signing import sign_pkcs11 as pkcs11 

253 except ImportError as e: 

254 raise RuntimeError( 

255 "PKCS #11 functionality requires the 'pkcs11' extra. " 

256 "Install with 'pip install model-signing[pkcs11]'." 

257 ) from e 

258 self._signer = pkcs11.Signer(pkcs11_uri, module_paths) 

259 return self 

260 

261 def use_pkcs11_certificate_signer( 

262 self, 

263 *, 

264 pkcs11_uri: str, 

265 signing_certificate: pathlib.Path, 

266 certificate_chain: Iterable[pathlib.Path], 

267 module_paths: Iterable[str] = frozenset(), 

268 ) -> Self: 

269 """Configures the signing to be performed using signing certificates. 

270 

271 The signer in this configuration is changed to one that performs signing 

272 using cryptographic certificates. 

273 

274 Args: 

275 pkcs11_uri: The PKCS #11 URI. 

276 signing_certificate: The path to the signing certificate. 

277 certificate_chain: Optional paths to other certificates to establish 

278 a chain of trust. 

279 module_paths: Optional list of paths of PKCS #11 modules. 

280 

281 Return: 

282 The new signing configuration. 

283 """ 

284 try: 

285 from model_signing._signing import sign_pkcs11 as pkcs11 

286 except ImportError as e: 

287 raise RuntimeError( 

288 "PKCS #11 functionality requires the 'pkcs11' extra. " 

289 "Install with 'pip install model-signing[pkcs11]'." 

290 ) from e 

291 

292 self._signer = pkcs11.CertSigner( 

293 pkcs11_uri, 

294 signing_certificate, 

295 certificate_chain, 

296 module_paths=module_paths, 

297 ) 

298 return self