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

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 (public instance and staging 

82 instance), signing with private keys and signing with signing certificates. 

83 Other signing modes can be added in the future. 

84 """ 

85 

86 def __init__(self): 

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

88 self._hashing_config = hashing.Config() 

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

90 self._signer = None 

91 

92 def sign( 

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

94 ): 

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

96 

97 Args: 

98 model_path: The path to the model to sign. 

99 signature_path: The path of the resulting signature. 

100 """ 

101 if not self._signer: 

102 self._signer = self.use_sigstore_signer() 

103 manifest = self._hashing_config.hash(model_path) 

104 payload = signing.Payload(manifest) 

105 signature = self._signer.sign(payload) 

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

107 

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

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

110 

111 Args: 

112 hashing_config: The new hashing configuration. 

113 

114 Returns: 

115 The new signing configuration. 

116 """ 

117 self._hashing_config = hashing_config 

118 return self 

119 

120 def use_sigstore_signer( 

121 self, 

122 *, 

123 oidc_issuer: Optional[str] = None, 

124 use_ambient_credentials: bool = False, 

125 use_staging: bool = False, 

126 force_oob: bool = False, 

127 identity_token: Optional[str] = None, 

128 client_id: Optional[str] = None, 

129 client_secret: Optional[str] = None, 

130 ) -> Self: 

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

132 

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

134 with Sigstore. 

135 

136 Args: 

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

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

139 Default is empty, relying on the Sigstore configuration. 

140 use_ambient_credentials: Use ambient credentials (also known as 

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

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

143 signer identity via OIDC will start. 

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

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

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

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

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

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

150 opened automatically if possible. 

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

152 taking precedence over any ambient credential or OAuth workflow. 

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

154 authentication. This is typically used to identify the 

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

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

157 used. 

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

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

160 required for confidential clients that need to prove their 

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

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

163 secret. 

164 

165 Return: 

166 The new signing configuration. 

167 """ 

168 self._signer = sigstore.Signer( 

169 oidc_issuer=oidc_issuer, 

170 use_ambient_credentials=use_ambient_credentials, 

171 use_staging=use_staging, 

172 identity_token=identity_token, 

173 force_oob=force_oob, 

174 client_id=client_id, 

175 client_secret=client_secret, 

176 ) 

177 return self 

178 

179 def use_elliptic_key_signer( 

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

181 ) -> Self: 

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

183 

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

185 using a private key based on elliptic curve cryptography. 

186 

187 Args: 

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

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

190 

191 Return: 

192 The new signing configuration. 

193 """ 

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

195 return self 

196 

197 def use_certificate_signer( 

198 self, 

199 *, 

200 private_key: hashing.PathLike, 

201 signing_certificate: hashing.PathLike, 

202 certificate_chain: Iterable[hashing.PathLike], 

203 ) -> Self: 

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

205 

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

207 using cryptographic signing certificates. 

208 

209 Args: 

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

211 signing_certificate: The path to the signing certificate. 

212 certificate_chain: Optional paths to other certificates to establish 

213 a chain of trust. 

214 

215 Return: 

216 The new signing configuration. 

217 """ 

218 self._signer = certificate.Signer( 

219 pathlib.Path(private_key), 

220 pathlib.Path(signing_certificate), 

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

222 ) 

223 return self 

224 

225 def use_pkcs11_signer( 

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

227 ) -> Self: 

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

229 

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

231 using a private key based on elliptic curve cryptography. 

232 

233 Args: 

234 pkcs11_uri: The PKCS11 URI. 

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

236 

237 Return: 

238 The new signing configuration. 

239 """ 

240 try: 

241 from model_signing._signing import sign_pkcs11 as pkcs11 

242 except ImportError as e: 

243 raise RuntimeError( 

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

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

246 ) from e 

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

248 return self 

249 

250 def use_pkcs11_certificate_signer( 

251 self, 

252 *, 

253 pkcs11_uri: str, 

254 signing_certificate: pathlib.Path, 

255 certificate_chain: Iterable[pathlib.Path], 

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

257 ) -> Self: 

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

259 

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

261 using cryptographic certificates. 

262 

263 Args: 

264 pkcs11_uri: The PKCS #11 URI. 

265 signing_certificate: The path to the signing certificate. 

266 certificate_chain: Optional paths to other certificates to establish 

267 a chain of trust. 

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

269 

270 Return: 

271 The new signing configuration. 

272 """ 

273 try: 

274 from model_signing._signing import sign_pkcs11 as pkcs11 

275 except ImportError as e: 

276 raise RuntimeError( 

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

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

279 ) from e 

280 

281 self._signer = pkcs11.CertSigner( 

282 pkcs11_uri, 

283 signing_certificate, 

284 certificate_chain, 

285 module_paths=module_paths, 

286 ) 

287 return self