3
3
import logging
4
4
import os
5
5
from abc import ABCMeta , abstractmethod
6
- from typing import Any , Callable , Dict , Optional , Type
6
+ from typing import Any , Callable , Dict , Optional , Type , cast
7
7
from urllib import parse
8
8
9
9
import securesystemslib .keys as sslib_keys
10
+ from securesystemslib .exceptions import UnsupportedLibraryError
10
11
from securesystemslib .formats import encode_canonical
11
12
from securesystemslib .hash import digest
12
13
from securesystemslib .signer ._key import Key , SSlibKey
13
14
from securesystemslib .signer ._signature import Signature
14
15
16
+ CRYPTO_IMPORT_ERROR = None
17
+ try :
18
+ from cryptography .hazmat .primitives .asymmetric .ec import (
19
+ ECDSA ,
20
+ EllipticCurvePrivateKey ,
21
+ )
22
+ from cryptography .hazmat .primitives .asymmetric .ed25519 import (
23
+ Ed25519PrivateKey ,
24
+ )
25
+ from cryptography .hazmat .primitives .asymmetric .padding import (
26
+ MGF1 ,
27
+ PSS ,
28
+ AsymmetricPadding ,
29
+ PKCS1v15 ,
30
+ )
31
+ from cryptography .hazmat .primitives .asymmetric .rsa import (
32
+ AsymmetricPadding ,
33
+ RSAPrivateKey ,
34
+ )
35
+ from cryptography .hazmat .primitives .asymmetric .types import PrivateKeyTypes
36
+ from cryptography .hazmat .primitives .hashes import (
37
+ SHA224 ,
38
+ SHA256 ,
39
+ SHA384 ,
40
+ SHA512 ,
41
+ HashAlgorithm ,
42
+ )
43
+ from cryptography .hazmat .primitives .serialization import (
44
+ load_pem_private_key ,
45
+ )
46
+ except ImportError :
47
+ CRYPTO_IMPORT_ERROR = "'pyca/cryptography' library required"
48
+
15
49
logger = logging .getLogger (__name__ )
16
50
17
51
# NOTE Signer dispatch table is defined here so it's usable by Signer,
@@ -164,6 +198,7 @@ class SSlibSigner(Signer):
164
198
165
199
def __init__ (self , key_dict : Dict ):
166
200
self .key_dict = key_dict
201
+ self ._crypto_signer = CryptoSigner .from_securesystemslib_key (key_dict )
167
202
168
203
@classmethod
169
204
def from_priv_key_uri (
@@ -212,6 +247,7 @@ def from_priv_key_uri(
212
247
213
248
keydict = public_key .to_securesystemslib_key ()
214
249
keydict ["keyval" ]["private" ] = private
250
+
215
251
return cls (keydict )
216
252
217
253
def sign (self , payload : bytes ) -> Signature :
@@ -225,5 +261,146 @@ def sign(self, payload: bytes) -> Signature:
225
261
securesystemslib.exceptions.UnsupportedAlgorithmError:
226
262
Signing errors.
227
263
"""
228
- sig_dict = sslib_keys .create_signature (self .key_dict , payload )
229
- return Signature (** sig_dict )
264
+ return self ._crypto_signer .sign (payload )
265
+
266
+
267
+ class CryptoSigner (Signer , metaclass = ABCMeta ):
268
+ """Base class for PYCA/cryptography Signer implementations."""
269
+
270
+ def __init__ (self , public_key : SSlibKey ):
271
+ if CRYPTO_IMPORT_ERROR :
272
+ raise UnsupportedLibraryError (CRYPTO_IMPORT_ERROR )
273
+
274
+ self .public_key = public_key
275
+
276
+ @classmethod
277
+ def from_securesystemslib_key (
278
+ cls , key_dict : Dict [str , Any ]
279
+ ) -> "CryptoSigner" :
280
+ """Factory to create CryptoSigner from securesystemslib private key dict."""
281
+ private = key_dict ["keyval" ]["private" ]
282
+ public_key = SSlibKey .from_securesystemslib_key (key_dict )
283
+
284
+ private_key : PrivateKeyTypes
285
+ if public_key .keytype == "rsa" :
286
+ private_key = cast (
287
+ RSAPrivateKey ,
288
+ load_pem_private_key (private .encode (), password = None ),
289
+ )
290
+ return RSASigner (public_key , private_key )
291
+
292
+ if public_key .keytype == "ecdsa" :
293
+ private_key = cast (
294
+ EllipticCurvePrivateKey ,
295
+ load_pem_private_key (private .encode (), password = None ),
296
+ )
297
+ return ECDSASigner (public_key , private_key )
298
+
299
+ if public_key .keytype == "ed25519" :
300
+ private_key = Ed25519PrivateKey .from_private_bytes (
301
+ bytes .fromhex (private )
302
+ )
303
+ return Ed25519Signer (public_key , private_key )
304
+
305
+ raise ValueError (f"unsupported keytype: { public_key .keytype } " )
306
+
307
+ @classmethod
308
+ def from_priv_key_uri (
309
+ cls ,
310
+ priv_key_uri : str ,
311
+ public_key : Key ,
312
+ secrets_handler : Optional [SecretsHandler ] = None ,
313
+ ) -> "Signer" :
314
+ # Do not raise NotImplementedError to appease pylint for all subclasses
315
+ raise RuntimeError ("use SSlibSigner.from_priv_key_uri" )
316
+
317
+
318
+ class RSASigner (CryptoSigner ):
319
+ """pyca/cryptography rsa signer implementation"""
320
+
321
+ def __init__ (self , public_key : SSlibKey , private_key : "RSAPrivateKey" ):
322
+ if public_key .scheme not in [
323
+ "rsassa-pss-sha224" ,
324
+ "rsassa-pss-sha256" ,
325
+ "rsassa-pss-sha384" ,
326
+ "rsassa-pss-sha512" ,
327
+ "rsa-pkcs1v15-sha224" ,
328
+ "rsa-pkcs1v15-sha256" ,
329
+ "rsa-pkcs1v15-sha384" ,
330
+ "rsa-pkcs1v15-sha512" ,
331
+ ]:
332
+ raise ValueError (f"unsupported scheme { public_key .scheme } " )
333
+
334
+ super ().__init__ (public_key )
335
+ self ._private_key = private_key
336
+ padding_name , hash_name = public_key .scheme .split ("-" )[1 :]
337
+ self ._algorithm = self ._get_hash_algorithm (hash_name )
338
+ self ._padding = self ._get_rsa_padding (padding_name , self ._algorithm )
339
+
340
+ @staticmethod
341
+ def _get_hash_algorithm (name : str ) -> "HashAlgorithm" :
342
+ """Helper to return hash algorithm for name."""
343
+ algorithm : HashAlgorithm
344
+ if name == "sha224" :
345
+ algorithm = SHA224 ()
346
+ if name == "sha256" :
347
+ algorithm = SHA256 ()
348
+ if name == "sha384" :
349
+ algorithm = SHA384 ()
350
+ if name == "sha512" :
351
+ algorithm = SHA512 ()
352
+
353
+ return algorithm
354
+
355
+ @staticmethod
356
+ def _get_rsa_padding (
357
+ name : str , hash_algorithm : "HashAlgorithm"
358
+ ) -> "AsymmetricPadding" :
359
+ """Helper to return rsa signature padding for name."""
360
+ padding : AsymmetricPadding
361
+ if name == "pss" :
362
+ padding = PSS (
363
+ mgf = MGF1 (hash_algorithm ), salt_length = PSS .DIGEST_LENGTH
364
+ )
365
+
366
+ if name == "pkcs1v15" :
367
+ padding = PKCS1v15 ()
368
+
369
+ return padding
370
+
371
+ def sign (self , payload : bytes ) -> Signature :
372
+ sig = self ._private_key .sign (payload , self ._padding , self ._algorithm )
373
+ return Signature (self .public_key .keyid , sig .hex ())
374
+
375
+
376
+ class ECDSASigner (CryptoSigner ):
377
+ """pyca/cryptography ecdsa signer implementation"""
378
+
379
+ def __init__ (
380
+ self , public_key : SSlibKey , private_key : "EllipticCurvePrivateKey"
381
+ ):
382
+ if public_key .scheme != "ecdsa-sha2-nistp256" :
383
+ raise ValueError (f"unsupported scheme { public_key .scheme } " )
384
+
385
+ super ().__init__ (public_key )
386
+ self ._private_key = private_key
387
+ self ._signature_algorithm = ECDSA (SHA256 ())
388
+
389
+ def sign (self , payload : bytes ) -> Signature :
390
+ sig = self ._private_key .sign (payload , self ._signature_algorithm )
391
+ return Signature (self .public_key .keyid , sig .hex ())
392
+
393
+
394
+ class Ed25519Signer (CryptoSigner ):
395
+ """pyca/cryptography ecdsa signer implementation"""
396
+
397
+ def __init__ (self , public_key : SSlibKey , private_key : "Ed25519PrivateKey" ):
398
+ if public_key .scheme != "ed25519" :
399
+ raise ValueError (f"unsupported scheme { public_key .scheme } " )
400
+
401
+ super ().__init__ (public_key )
402
+ self ._private_key = private_key
403
+
404
+ def sign (self , payload : bytes ) -> Signature :
405
+ sig = self ._private_key .sign (payload )
406
+ return Signature (self .public_key .keyid , sig .hex ())
0 commit comments