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

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

52 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 

48 

49from model_signing import hashing 

50from model_signing._signing import sign_certificate as certificate 

51from model_signing._signing import sign_ec_key as ec_key 

52from model_signing._signing import sign_sigstore as sigstore 

53from model_signing._signing import signing 

54 

55 

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

57 from typing import Self 

58else: 

59 from typing_extensions import Self 

60 

61 

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

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

64 

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

66 configuration from `model_signing.hashing`. 

67 

68 The resulting signature is in the Sigstore bundle format. 

69 

70 Args: 

71 model_path: the path to the model to sign. 

72 signature_path: the path of the resulting signature. 

73 """ 

74 Config().sign(model_path, signature_path) 

75 

76 

77class Config: 

78 """Configuration to use when signing models. 

79 

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

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

82 signing with signing certificates, and signing with custom 

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

84 This allows users to bring their own trust configuration 

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

86 added in the future. 

87 """ 

88 

89 def __init__(self): 

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

91 self._hashing_config = hashing.Config() 

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

93 self._signer = None 

94 

95 def sign( 

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

97 ): 

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

99 

100 Args: 

101 model_path: The path to the model to sign. 

102 signature_path: The path of the resulting signature. 

103 """ 

104 if not self._signer: 

105 self.use_sigstore_signer() 

106 manifest = self._hashing_config.hash(model_path) 

107 payload = signing.Payload(manifest) 

108 signature = self._signer.sign(payload) 

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

110 

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

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

113 

114 Args: 

115 hashing_config: The new hashing configuration. 

116 

117 Returns: 

118 The new signing configuration. 

119 """ 

120 self._hashing_config = hashing_config 

121 return self 

122 

123 def use_sigstore_signer( 

124 self, 

125 *, 

126 oidc_issuer: str | None = None, 

127 use_ambient_credentials: bool = False, 

128 use_staging: bool = False, 

129 force_oob: bool = False, 

130 identity_token: str | None = None, 

131 client_id: str | None = None, 

132 client_secret: str | None = None, 

133 trust_config: pathlib.Path | None = None, 

134 ) -> Self: 

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

136 

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

138 with Sigstore. 

139 

140 Args: 

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

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

143 Default is empty, relying on the Sigstore configuration. 

144 use_ambient_credentials: Use ambient credentials (also known as 

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

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

147 signer identity via OIDC will start. 

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

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

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

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

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

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

154 opened automatically if possible. 

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

156 taking precedence over any ambient credential or OAuth workflow. 

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

158 authentication. This is typically used to identify the 

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

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

161 used. 

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

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

164 required for confidential clients that need to prove their 

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

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

167 secret. 

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

169 the signature verification process will rely on the supplied 

170 PKI and trust configurations, instead of the default Sigstore 

171 setup. If not specified, the default Sigstore configuration 

172 is used. 

173 

174 Return: 

175 The new signing configuration. 

176 """ 

177 self._signer = sigstore.Signer( 

178 oidc_issuer=oidc_issuer, 

179 use_ambient_credentials=use_ambient_credentials, 

180 use_staging=use_staging, 

181 identity_token=identity_token, 

182 force_oob=force_oob, 

183 client_id=client_id, 

184 client_secret=client_secret, 

185 trust_config=trust_config, 

186 ) 

187 return self 

188 

189 def use_elliptic_key_signer( 

190 self, *, private_key: hashing.PathLike, password: str | None = None 

191 ) -> Self: 

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

193 

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

195 using a private key based on elliptic curve cryptography. 

196 

197 Args: 

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

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

200 

201 Return: 

202 The new signing configuration. 

203 """ 

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

205 return self 

206 

207 def use_certificate_signer( 

208 self, 

209 *, 

210 private_key: hashing.PathLike, 

211 signing_certificate: hashing.PathLike, 

212 certificate_chain: Iterable[hashing.PathLike], 

213 ) -> Self: 

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

215 

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

217 using cryptographic signing certificates. 

218 

219 Args: 

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

221 signing_certificate: The path to the signing certificate. 

222 certificate_chain: Optional paths to other certificates to establish 

223 a chain of trust. 

224 

225 Return: 

226 The new signing configuration. 

227 """ 

228 self._signer = certificate.Signer( 

229 pathlib.Path(private_key), 

230 pathlib.Path(signing_certificate), 

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

232 ) 

233 return self 

234 

235 def use_pkcs11_signer( 

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

237 ) -> Self: 

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

239 

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

241 using a private key based on elliptic curve cryptography. 

242 

243 Args: 

244 pkcs11_uri: The PKCS11 URI. 

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

246 

247 Return: 

248 The new signing configuration. 

249 """ 

250 try: 

251 from model_signing._signing import sign_pkcs11 as pkcs11 

252 except ImportError as e: 

253 raise RuntimeError( 

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

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

256 ) from e 

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

258 return self 

259 

260 def use_pkcs11_certificate_signer( 

261 self, 

262 *, 

263 pkcs11_uri: str, 

264 signing_certificate: pathlib.Path, 

265 certificate_chain: Iterable[pathlib.Path], 

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

267 ) -> Self: 

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

269 

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

271 using cryptographic certificates. 

272 

273 Args: 

274 pkcs11_uri: The PKCS #11 URI. 

275 signing_certificate: The path to the signing certificate. 

276 certificate_chain: Optional paths to other certificates to establish 

277 a chain of trust. 

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

279 

280 Return: 

281 The new signing configuration. 

282 """ 

283 try: 

284 from model_signing._signing import sign_pkcs11 as pkcs11 

285 except ImportError as e: 

286 raise RuntimeError( 

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

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

289 ) from e 

290 

291 self._signer = pkcs11.CertSigner( 

292 pkcs11_uri, 

293 signing_certificate, 

294 certificate_chain, 

295 module_paths=module_paths, 

296 ) 

297 return self