1
1
"""Signer implementation for pyca/cryptography signing. """
2
2
3
3
import logging
4
- from abc import ABCMeta
5
- from typing import Any , Dict , Optional , cast
4
+ from dataclasses import astuple , dataclass
5
+ from typing import Any , Dict , Optional , Union
6
6
from urllib import parse
7
7
8
8
from securesystemslib .exceptions import UnsupportedLibraryError
53
53
logger = logging .getLogger (__name__ )
54
54
55
55
56
- class CryptoSigner (Signer , metaclass = ABCMeta ):
57
- """Base class for PYCA/cryptography Signer implementations."""
56
+ @dataclass
57
+ class _RSASignArgs :
58
+ padding : "AsymmetricPadding"
59
+ hash_algo : "HashAlgorithm"
60
+
61
+
62
+ @dataclass
63
+ class _ECDSASignArgs :
64
+ sig_algo : "ECDSA"
65
+
66
+
67
+ @dataclass
68
+ class _NoSignArgs :
69
+ pass
70
+
71
+
72
+ def _get_hash_algorithm (name : str ) -> "HashAlgorithm" :
73
+ """Helper to return hash algorithm for name."""
74
+ algorithm : HashAlgorithm
75
+ if name == "sha224" :
76
+ algorithm = SHA224 ()
77
+ if name == "sha256" :
78
+ algorithm = SHA256 ()
79
+ if name == "sha384" :
80
+ algorithm = SHA384 ()
81
+ if name == "sha512" :
82
+ algorithm = SHA512 ()
83
+
84
+ return algorithm
85
+
86
+
87
+ def _get_rsa_padding (
88
+ name : str , hash_algorithm : "HashAlgorithm"
89
+ ) -> "AsymmetricPadding" :
90
+ """Helper to return rsa signature padding for name."""
91
+ padding : AsymmetricPadding
92
+ if name == "pss" :
93
+ padding = PSS (mgf = MGF1 (hash_algorithm ), salt_length = PSS .DIGEST_LENGTH )
94
+
95
+ if name == "pkcs1v15" :
96
+ padding = PKCS1v15 ()
97
+
98
+ return padding
99
+
100
+
101
+ class CryptoSigner (Signer ):
102
+ """PYCA/cryptography Signer implementations.
103
+
104
+ A CryptoSigner can be created from:
105
+
106
+ a. private key file -- ``Signer.from_priv_key_uri()``
107
+
108
+ URI has the format "file:<PATH>?encrypted=[true|false]", where
109
+ PATH is the path to a file with private key data in a standard
110
+ PEM/PKCS8 format.
111
+
112
+ A related public key must be passed.
113
+
114
+ If ``encrypted=true``, the optional secrets handler is expected to
115
+ return a decryption password.
116
+
117
+ b. newly generated key pair -- ``CryptoSigner.generate_*()``
118
+
119
+ c. existing pyca/cryptography private key object -- ``CryptoSigner()``
120
+
121
+ """
58
122
59
123
FILE_URI_SCHEME = "file"
60
124
61
- def __init__ (self , public_key : SSlibKey ):
125
+ def __init__ (
126
+ self ,
127
+ private_key : "PrivateKeyTypes" ,
128
+ public_key : Optional [SSlibKey ] = None ,
129
+ ):
62
130
if CRYPTO_IMPORT_ERROR :
63
131
raise UnsupportedLibraryError (CRYPTO_IMPORT_ERROR )
64
132
133
+ if public_key is None :
134
+ public_key = SSlibKey ._from_crypto_public_key (
135
+ private_key .public_key (), None , None
136
+ )
137
+
138
+ self ._private_key : PrivateKeyTypes
139
+ self ._sign_args : Union [_RSASignArgs , _ECDSASignArgs , _NoSignArgs ]
140
+
141
+ if public_key .keytype == "rsa" and public_key .scheme in [
142
+ "rsassa-pss-sha224" ,
143
+ "rsassa-pss-sha256" ,
144
+ "rsassa-pss-sha384" ,
145
+ "rsassa-pss-sha512" ,
146
+ "rsa-pkcs1v15-sha224" ,
147
+ "rsa-pkcs1v15-sha256" ,
148
+ "rsa-pkcs1v15-sha384" ,
149
+ "rsa-pkcs1v15-sha512" ,
150
+ ]:
151
+ if not isinstance (private_key , RSAPrivateKey ):
152
+ raise ValueError (f"invalid rsa key: { type (private_key )} " )
153
+
154
+ padding_name , hash_name = public_key .scheme .split ("-" )[1 :]
155
+ hash_algo = _get_hash_algorithm (hash_name )
156
+ padding = _get_rsa_padding (padding_name , hash_algo )
157
+ self ._sign_args = _RSASignArgs (padding , hash_algo )
158
+ self ._private_key = private_key
159
+
160
+ elif (
161
+ public_key .keytype == "ecdsa"
162
+ and public_key .scheme == "ecdsa-sha2-nistp256"
163
+ ):
164
+ if not isinstance (private_key , EllipticCurvePrivateKey ):
165
+ raise ValueError (f"invalid ecdsa key: { type (private_key )} " )
166
+
167
+ signature_algorithm = ECDSA (SHA256 ())
168
+ self ._sign_args = _ECDSASignArgs (signature_algorithm )
169
+ self ._private_key = private_key
170
+
171
+ elif public_key .keytype == "ed25519" and public_key .scheme == "ed25519" :
172
+ if not isinstance (private_key , Ed25519PrivateKey ):
173
+ raise ValueError (f"invalid ed25519 key: { type (private_key )} " )
174
+
175
+ self ._sign_args = _NoSignArgs ()
176
+ self ._private_key = private_key
177
+
178
+ else :
179
+ raise ValueError (
180
+ f"unsupported public key { public_key .keytype } /{ public_key .scheme } "
181
+ )
182
+
65
183
self .public_key = public_key
66
184
67
185
@classmethod
@@ -73,49 +191,18 @@ def from_securesystemslib_key(
73
191
public_key = SSlibKey .from_securesystemslib_key (key_dict )
74
192
75
193
private_key : PrivateKeyTypes
76
- if public_key .keytype == "rsa" :
77
- private_key = cast (
78
- RSAPrivateKey ,
79
- load_pem_private_key (private .encode (), password = None ),
80
- )
81
- return _RSASigner (public_key , private_key )
194
+ if public_key .keytype in ["rsa" , "ecdsa" ]:
195
+ private_key = load_pem_private_key (private .encode (), password = None )
82
196
83
- if public_key .keytype == "ecdsa" :
84
- private_key = cast (
85
- EllipticCurvePrivateKey ,
86
- load_pem_private_key (private .encode (), password = None ),
87
- )
88
- return _ECDSASigner (public_key , private_key )
89
-
90
- if public_key .keytype == "ed25519" :
197
+ elif public_key .keytype == "ed25519" :
91
198
private_key = Ed25519PrivateKey .from_private_bytes (
92
199
bytes .fromhex (private )
93
200
)
94
- return _Ed25519Signer (public_key , private_key )
95
-
96
- raise ValueError (f"unsupported keytype: { public_key .keytype } " )
97
-
98
- @classmethod
99
- def _from_pem (
100
- cls , private_pem : bytes , secret : Optional [bytes ], public_key : SSlibKey
101
- ):
102
- """Helper factory to create CryptoSigner from private PEM."""
103
- private_key = load_pem_private_key (private_pem , secret )
104
-
105
- if public_key .keytype == "rsa" :
106
- return _RSASigner (public_key , cast (RSAPrivateKey , private_key ))
107
-
108
- if public_key .keytype == "ecdsa" :
109
- return _ECDSASigner (
110
- public_key , cast (EllipticCurvePrivateKey , private_key )
111
- )
112
201
113
- if public_key .keytype == "ed25519" :
114
- return _Ed25519Signer (
115
- public_key , cast (Ed25519PrivateKey , private_key )
116
- )
202
+ else :
203
+ raise ValueError (f"unsupported keytype: { public_key .keytype } " )
117
204
118
- raise ValueError ( f"unsupported keytype: { public_key . keytype } " )
205
+ return CryptoSigner ( private_key , public_key )
119
206
120
207
@classmethod
121
208
def from_priv_key_uri (
@@ -167,7 +254,8 @@ def from_priv_key_uri(
167
254
with open (uri .path , "rb" ) as f :
168
255
private_pem = f .read ()
169
256
170
- return cls ._from_pem (private_pem , secret , public_key )
257
+ private_key = load_pem_private_key (private_pem , secret )
258
+ return CryptoSigner (private_key , public_key )
171
259
172
260
@staticmethod
173
261
def generate_ed25519 (
@@ -191,7 +279,7 @@ def generate_ed25519(
191
279
public_key = SSlibKey ._from_crypto_public_key ( # pylint: disable=protected-access
192
280
private_key .public_key (), keyid , "ed25519"
193
281
)
194
- return _Ed25519Signer ( public_key , private_key )
282
+ return CryptoSigner ( private_key , public_key )
195
283
196
284
@staticmethod
197
285
def generate_rsa (
@@ -222,7 +310,7 @@ def generate_rsa(
222
310
public_key = SSlibKey ._from_crypto_public_key ( # pylint: disable=protected-access
223
311
private_key .public_key (), keyid , scheme
224
312
)
225
- return _RSASigner ( public_key , private_key )
313
+ return CryptoSigner ( private_key , public_key )
226
314
227
315
@staticmethod
228
316
def generate_ecdsa (
@@ -246,95 +334,8 @@ def generate_ecdsa(
246
334
public_key = SSlibKey ._from_crypto_public_key ( # pylint: disable=protected-access
247
335
private_key .public_key (), keyid , "ecdsa-sha2-nistp256"
248
336
)
249
- return _ECDSASigner (public_key , private_key )
250
-
251
-
252
- class _RSASigner (CryptoSigner ):
253
- """Internal pyca/cryptography rsa signer implementation"""
254
-
255
- def __init__ (self , public_key : SSlibKey , private_key : "RSAPrivateKey" ):
256
- if public_key .scheme not in [
257
- "rsassa-pss-sha224" ,
258
- "rsassa-pss-sha256" ,
259
- "rsassa-pss-sha384" ,
260
- "rsassa-pss-sha512" ,
261
- "rsa-pkcs1v15-sha224" ,
262
- "rsa-pkcs1v15-sha256" ,
263
- "rsa-pkcs1v15-sha384" ,
264
- "rsa-pkcs1v15-sha512" ,
265
- ]:
266
- raise ValueError (f"unsupported scheme { public_key .scheme } " )
267
-
268
- super ().__init__ (public_key )
269
- self ._private_key = private_key
270
- padding_name , hash_name = public_key .scheme .split ("-" )[1 :]
271
- self ._algorithm = self ._get_hash_algorithm (hash_name )
272
- self ._padding = self ._get_rsa_padding (padding_name , self ._algorithm )
273
-
274
- @staticmethod
275
- def _get_hash_algorithm (name : str ) -> "HashAlgorithm" :
276
- """Helper to return hash algorithm for name."""
277
- algorithm : HashAlgorithm
278
- if name == "sha224" :
279
- algorithm = SHA224 ()
280
- if name == "sha256" :
281
- algorithm = SHA256 ()
282
- if name == "sha384" :
283
- algorithm = SHA384 ()
284
- if name == "sha512" :
285
- algorithm = SHA512 ()
286
-
287
- return algorithm
288
-
289
- @staticmethod
290
- def _get_rsa_padding (
291
- name : str , hash_algorithm : "HashAlgorithm"
292
- ) -> "AsymmetricPadding" :
293
- """Helper to return rsa signature padding for name."""
294
- padding : AsymmetricPadding
295
- if name == "pss" :
296
- padding = PSS (
297
- mgf = MGF1 (hash_algorithm ), salt_length = PSS .DIGEST_LENGTH
298
- )
299
-
300
- if name == "pkcs1v15" :
301
- padding = PKCS1v15 ()
302
-
303
- return padding
304
-
305
- def sign (self , payload : bytes ) -> Signature :
306
- sig = self ._private_key .sign (payload , self ._padding , self ._algorithm )
307
- return Signature (self .public_key .keyid , sig .hex ())
308
-
309
-
310
- class _ECDSASigner (CryptoSigner ):
311
- """Internal pyca/cryptography ecdsa signer implementation"""
312
-
313
- def __init__ (
314
- self , public_key : SSlibKey , private_key : "EllipticCurvePrivateKey"
315
- ):
316
- if public_key .scheme != "ecdsa-sha2-nistp256" :
317
- raise ValueError (f"unsupported scheme { public_key .scheme } " )
318
-
319
- super ().__init__ (public_key )
320
- self ._private_key = private_key
321
- self ._signature_algorithm = ECDSA (SHA256 ())
322
-
323
- def sign (self , payload : bytes ) -> Signature :
324
- sig = self ._private_key .sign (payload , self ._signature_algorithm )
325
- return Signature (self .public_key .keyid , sig .hex ())
326
-
327
-
328
- class _Ed25519Signer (CryptoSigner ):
329
- """Internal pyca/cryptography ecdsa signer implementation"""
330
-
331
- def __init__ (self , public_key : SSlibKey , private_key : "Ed25519PrivateKey" ):
332
- if public_key .scheme != "ed25519" :
333
- raise ValueError (f"unsupported scheme { public_key .scheme } " )
334
-
335
- super ().__init__ (public_key )
336
- self ._private_key = private_key
337
+ return CryptoSigner (private_key , public_key )
337
338
338
339
def sign (self , payload : bytes ) -> Signature :
339
- sig = self ._private_key .sign (payload )
340
+ sig = self ._private_key .sign (payload , * astuple ( self . _sign_args )) # type: ignore
340
341
return Signature (self .public_key .keyid , sig .hex ())
0 commit comments