1#!/usr/bin/env python
2
3"""
4<Program Name>
5  keys.py
6
7<Author>
8  Vladimir Diaz <vladimir.v.diaz@gmail.com>
9
10<Started>
11  October 4, 2013.
12
13<Copyright>
14  See LICENSE for licensing information.
15
16<Purpose>
17  The goal of this module is to centralize cryptographic key routines and their
18  supported operations (e.g., creating and verifying signatures).  This module
19  is designed to support multiple public-key algorithms, such as RSA, Ed25519,
20  and ECDSA, and multiple cryptography libraries.  Which cryptography library
21  to use is determined by the default, or user modified, values set in
22  'settings.py'
23
24  https://en.wikipedia.org/wiki/RSA_(algorithm)
25  http://ed25519.cr.yp.to/
26  https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
27
28  The (RSA, ECDSA and Ed25519)-related functions provided include
29  generate_rsa_key(), generate_ed25519_key(), generate_ecdsa_key(),
30  create_signature(), and verify_signature().  The cryptography libraries
31  called by 'securesystemslib.keys.py' generate the actual keys and the
32  functions listed above can be viewed as the easy-to-use public interface.
33
34  Additional functions contained here include format_keyval_to_metadata() and
35  format_metadata_to_key().  These last two functions produce or use keys
36  compatible with the key structures listed in Metadata files.  The key
37  generation functions return a dictionary containing all the information needed
38  of keys, such as public & private keys, and a keyID.  create_signature()
39  and verify_signature() are supplemental functions needed for generating
40  signatures and verifying them.
41
42  Key IDs are used as identifiers for keys (e.g., RSA key).  They are the
43  hexadecimal representation of the hash of the key object (specifically, the
44  key object containing only the public key).  Review the '_get_keyid()'
45  function of this module to see precisely how keyids are generated.  One may
46  get the keyid of a key object by simply accessing the dictionary's 'keyid'
47  key (i.e., rsakey['keyid']).
48 """
49
50# Help with Python 3 compatibility, where the print statement is a function, an
51# implicit relative import is invalid, and the '/' operator performs true
52# division.  Example:  print 'hello world' raises a 'SyntaxError' exception.
53from __future__ import print_function
54from __future__ import absolute_import
55from __future__ import division
56from __future__ import unicode_literals
57
58# Required for hexadecimal conversions.  Signatures and public/private keys are
59# hexlified.
60import binascii
61
62# NOTE:  'warnings' needed to temporarily suppress user warnings raised by
63# 'pynacl' (as of version 0.2.3).
64# http://docs.python.org/2/library/warnings.html#temporarily-suppressing-warnings
65import warnings
66import logging
67
68# Try to import the pyca/Cryptography module (pyca_crypto_keys.py), which is
69# used for general-purpose cryptography and generation of RSA keys and
70# signatures.
71try:
72  import securesystemslib.pyca_crypto_keys
73
74except ImportError: #pragma: no cover
75  pass
76
77# Import the PyNaCl library, if available.  It is recommended this library be
78# used over the pure python implementation of ed25519, due to its speedier
79# routines and side-channel protections available in the libsodium library.
80
81# NOTE: Version 0.2.3 of 'pynacl' prints: "UserWarning: reimporting '...' might
82# overwrite older definitions." when importing 'nacl.signing' below.  Suppress
83# user warnings temporarily (at least until this issue is fixed).
84with warnings.catch_warnings():
85  warnings.simplefilter('ignore')
86  try:
87    import nacl
88    import nacl.signing
89    USE_PYNACL = True
90
91  # PyNaCl's 'cffi' dependency may raise an 'IOError' exception when importing
92  # 'nacl.signing'.
93  except (ImportError, IOError): # pragma: no cover
94    USE_PYNACL = False
95    pass
96
97# The optimized version of the Ed25519 library provided by default is imported
98# regardless of the availability of PyNaCl.
99import securesystemslib.ed25519_keys
100
101try:
102  import securesystemslib.ecdsa_keys
103
104except ImportError: #pragma: no cover
105  pass
106
107import securesystemslib.exceptions
108
109# Digest objects needed to generate hashes.
110import securesystemslib.hash
111
112# Perform format checks of argument objects.
113import securesystemslib.formats
114
115# The hash algorithm to use in the generation of keyids.
116_KEY_ID_HASH_ALGORITHM = 'sha256'
117
118# Recommended RSA key sizes:
119# http://www.emc.com/emc-plus/rsa-labs/historical/twirl-and-rsa-key-size.htm#table1
120# According to the document above, revised May 6, 2003, RSA keys of
121# size 3072 provide security through 2031 and beyond.
122_DEFAULT_RSA_KEY_BITS = 3072
123
124logger = logging.getLogger('securesystemslib_keys')
125
126
127def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS, scheme='rsassa-pss-sha256'):
128  """
129  <Purpose>
130    Generate public and private RSA keys, with modulus length 'bits'.  In
131    addition, a keyid identifier for the RSA key is generated.  The object
132    returned conforms to 'securesystemslib.formats.RSAKEY_SCHEMA' and has the
133    form:
134
135    {'keytype': 'rsa',
136     'scheme': 'rsassa-pss-sha256',
137     'keyid': keyid,
138     'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...',
139                'private': '-----BEGIN RSA PRIVATE KEY----- ...'}}
140
141    The public and private keys are strings in PEM format.
142
143    Although the PyCA cryptography library and/or its crypto backend might set
144    a minimum key size, generate() enforces a minimum key size of 2048 bits.
145    If 'bits' is unspecified, a 3072-bit RSA key is generated, which is the key
146    size recommended by securesystemslib.  These key size restrictions are only
147    enforced for keys generated within securesystemslib.  RSA keys with sizes
148    lower than what we recommended may still be imported (e.g., with
149    import_rsakey_from_pem().
150
151    >>> rsa_key = generate_rsa_key(bits=2048)
152    >>> securesystemslib.formats.RSAKEY_SCHEMA.matches(rsa_key)
153    True
154
155    >>> public = rsa_key['keyval']['public']
156    >>> private = rsa_key['keyval']['private']
157    >>> securesystemslib.formats.PEMRSA_SCHEMA.matches(public)
158    True
159    >>> securesystemslib.formats.PEMRSA_SCHEMA.matches(private)
160    True
161
162  <Arguments>
163    bits:
164      The key size, or key length, of the RSA key.  'bits' must be 2048, or
165      greater, and a multiple of 256.
166
167    scheme:
168      The signature scheme used by the key.  It must be one of
169      ['rsassa-pss-sha256'].
170
171  <Exceptions>
172    securesystemslib.exceptions.FormatError, if 'bits' is improperly or invalid
173    (i.e., not an integer and not at least 2048).
174
175    ValueError, if an exception occurs after calling the RSA key generation
176    routine.  The 'ValueError' exception is raised by the key generation
177    function of the cryptography library called.
178
179  <Side Effects>
180    None.
181
182  <Returns>
183    A dictionary containing the RSA keys and other identifying information.
184    Conforms to 'securesystemslib.formats.RSAKEY_SCHEMA'.
185  """
186
187  # Does 'bits' have the correct format?  This check will ensure 'bits'
188  # conforms to 'securesystemslib.formats.RSAKEYBITS_SCHEMA'.  'bits' must be
189  # an integer object, with a minimum value of 2048.  Raise
190  # 'securesystemslib.exceptions.FormatError' if the check fails.
191  securesystemslib.formats.RSAKEYBITS_SCHEMA.check_match(bits)
192  securesystemslib.formats.RSA_SCHEME_SCHEMA.check_match(scheme)
193
194  # Begin building the RSA key dictionary.
195  rsakey_dict = {}
196  keytype = 'rsa'
197  public = None
198  private = None
199
200  # Generate the public and private RSA keys.  The pyca/cryptography module is
201  # used to generate the actual key.  Raise 'ValueError' if 'bits' is less than
202  # 1024, although a 2048-bit minimum is enforced by
203  # securesystemslib.formats.RSAKEYBITS_SCHEMA.check_match().
204  public, private = securesystemslib.pyca_crypto_keys.generate_rsa_public_and_private(bits)
205
206  # When loading in PEM keys, extract_pem() is called, which strips any
207  # leading or trailing new line characters. Do the same here before generating
208  # the keyid.
209  public =  extract_pem(public, private_pem=False)
210  private = extract_pem(private, private_pem=True)
211
212  # Generate the keyid of the RSA key.  Note: The private key material is not
213  # included in the generation of the 'keyid' identifier.  Convert any '\r\n'
214  # (e.g., Windows) newline characters to '\n' so that a consistent keyid is
215  # generated.
216  key_value = {'public': public.replace('\r\n', '\n'),
217               'private': ''}
218  keyid = _get_keyid(keytype, scheme, key_value)
219
220  # Build the 'rsakey_dict' dictionary.  Update 'key_value' with the RSA
221  # private key prior to adding 'key_value' to 'rsakey_dict'.
222  key_value['private'] = private
223
224  rsakey_dict['keytype'] = keytype
225  rsakey_dict['scheme'] = scheme
226  rsakey_dict['keyid'] = keyid
227  rsakey_dict['keyid_hash_algorithms'] = securesystemslib.settings.HASH_ALGORITHMS
228  rsakey_dict['keyval'] = key_value
229
230  return rsakey_dict
231
232
233
234
235
236def generate_ecdsa_key(scheme='ecdsa-sha2-nistp256'):
237  """
238  <Purpose>
239    Generate public and private ECDSA keys, with NIST P-256 + SHA256 (for
240    hashing) being the default scheme.  In addition, a keyid identifier for the
241    ECDSA key is generated.  The object returned conforms to
242    'securesystemslib.formats.ECDSAKEY_SCHEMA' and has the form:
243
244    {'keytype': 'ecdsa-sha2-nistp256',
245     'scheme', 'ecdsa-sha2-nistp256',
246     'keyid': keyid,
247     'keyval': {'public': '',
248                'private': ''}}
249
250    The public and private keys are strings in TODO format.
251
252    >>> ecdsa_key = generate_ecdsa_key(scheme='ecdsa-sha2-nistp256')
253    >>> securesystemslib.formats.ECDSAKEY_SCHEMA.matches(ecdsa_key)
254    True
255
256  <Arguments>
257    scheme:
258      The ECDSA signature scheme.  By default, ECDSA NIST P-256 is used, with
259      SHA256 for hashing.
260
261  <Exceptions>
262    securesystemslib.exceptions.FormatError, if 'scheme' is improperly
263    formatted or invalid (i.e., not one of the supported ECDSA signature
264    schemes).
265
266  <Side Effects>
267    None.
268
269  <Returns>
270    A dictionary containing the ECDSA keys and other identifying information.
271    Conforms to 'securesystemslib.formats.ECDSAKEY_SCHEMA'.
272  """
273
274  # Does 'scheme' have the correct format?
275  # This check will ensure 'scheme' is properly formatted and is a supported
276  # ECDSA signature scheme.  Raise 'securesystemslib.exceptions.FormatError' if
277  # the check fails.
278  securesystemslib.formats.ECDSA_SCHEME_SCHEMA.check_match(scheme)
279
280  # Begin building the ECDSA key dictionary.
281  ecdsa_key = {}
282  keytype = 'ecdsa-sha2-nistp256'
283  public = None
284  private = None
285
286  # Generate the public and private ECDSA keys with one of the supported
287  # libraries.
288  public, private = \
289    securesystemslib.ecdsa_keys.generate_public_and_private(scheme)
290
291  # Generate the keyid of the Ed25519 key.  'key_value' corresponds to the
292  # 'keyval' entry of the 'Ed25519KEY_SCHEMA' dictionary.  The private key
293  # information is not included in the generation of the 'keyid' identifier.
294  # Convert any '\r\n' (e.g., Windows) newline characters to '\n' so that a
295  # consistent keyid is generated.
296  key_value = {'public': public.replace('\r\n', '\n'),
297               'private': ''}
298  keyid = _get_keyid(keytype, scheme, key_value)
299
300  # Build the 'ed25519_key' dictionary.  Update 'key_value' with the Ed25519
301  # private key prior to adding 'key_value' to 'ed25519_key'.
302
303  key_value['private'] = private
304
305  ecdsa_key['keytype'] = keytype
306  ecdsa_key['scheme'] = scheme
307  ecdsa_key['keyid'] = keyid
308  ecdsa_key['keyval'] = key_value
309
310  # Add "keyid_hash_algorithms" so that equal ECDSA keys with different keyids
311  # can be associated using supported keyid_hash_algorithms.
312  ecdsa_key['keyid_hash_algorithms'] = \
313      securesystemslib.settings.HASH_ALGORITHMS
314
315  return ecdsa_key
316
317
318
319
320
321def generate_ed25519_key(scheme='ed25519'):
322  """
323  <Purpose>
324    Generate public and private ED25519 keys, both of length 32-bytes, although
325    they are hexlified to 64 bytes.  In addition, a keyid identifier generated
326    for the returned ED25519 object.  The object returned conforms to
327    'securesystemslib.formats.ED25519KEY_SCHEMA' and has the form:
328
329    {'keytype': 'ed25519',
330     'scheme': 'ed25519',
331     'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
332     'keyval': {'public': '9ccf3f02b17f82febf5dd3bab878b767d8408...',
333                'private': 'ab310eae0e229a0eceee3947b6e0205dfab3...'}}
334
335    >>> ed25519_key = generate_ed25519_key()
336    >>> securesystemslib.formats.ED25519KEY_SCHEMA.matches(ed25519_key)
337    True
338    >>> len(ed25519_key['keyval']['public'])
339    64
340    >>> len(ed25519_key['keyval']['private'])
341    64
342
343  <Arguments>
344    scheme:
345      The signature scheme used by the generated Ed25519 key.
346
347  <Exceptions>
348    None.
349
350  <Side Effects>
351    The ED25519 keys are generated by calling either the optimized pure Python
352    implementation of ed25519, or the ed25519 routines provided by 'pynacl'.
353
354  <Returns>
355    A dictionary containing the ED25519 keys and other identifying information.
356    Conforms to 'securesystemslib.formats.ED25519KEY_SCHEMA'.
357  """
358
359  # Are the arguments properly formatted?  If not, raise an
360  # 'securesystemslib.exceptions.FormatError' exceptions.
361  securesystemslib.formats.ED25519_SIG_SCHEMA.check_match(scheme)
362
363  # Begin building the Ed25519 key dictionary.
364  ed25519_key = {}
365  keytype = 'ed25519'
366  public = None
367  private = None
368
369  # Generate the public and private Ed25519 key with the 'pynacl' library.
370  # Unlike in the verification of Ed25519 signatures, do not fall back to the
371  # optimized, pure python implementation provided by PyCA.  Ed25519 should
372  # always be generated with a backend like libsodium to prevent side-channel
373  # attacks.
374  public, private = \
375    securesystemslib.ed25519_keys.generate_public_and_private()
376
377  # Generate the keyid of the Ed25519 key.  'key_value' corresponds to the
378  # 'keyval' entry of the 'Ed25519KEY_SCHEMA' dictionary.  The private key
379  # information is not included in the generation of the 'keyid' identifier.
380  key_value = {'public': binascii.hexlify(public).decode(),
381               'private': ''}
382  keyid = _get_keyid(keytype, scheme, key_value)
383
384  # Build the 'ed25519_key' dictionary.  Update 'key_value' with the Ed25519
385  # private key prior to adding 'key_value' to 'ed25519_key'.
386  key_value['private'] = binascii.hexlify(private).decode()
387
388  ed25519_key['keytype'] = keytype
389  ed25519_key['scheme'] = scheme
390  ed25519_key['keyid'] = keyid
391  ed25519_key['keyid_hash_algorithms'] = securesystemslib.settings.HASH_ALGORITHMS
392  ed25519_key['keyval'] = key_value
393
394  return ed25519_key
395
396
397
398
399
400def format_keyval_to_metadata(keytype, scheme, key_value, private=False):
401  """
402  <Purpose>
403    Return a dictionary conformant to 'securesystemslib.formats.KEY_SCHEMA'.
404    If 'private' is True, include the private key.  The dictionary
405    returned has the form:
406
407    {'keytype': keytype,
408     'scheme' : scheme,
409     'keyval': {'public': '...',
410                'private': '...'}}
411
412    or if 'private' is False:
413
414    {'keytype': keytype,
415     'scheme': scheme,
416     'keyval': {'public': '...',
417                'private': ''}}
418
419    >>> ed25519_key = generate_ed25519_key()
420    >>> key_val = ed25519_key['keyval']
421    >>> keytype = ed25519_key['keytype']
422    >>> scheme = ed25519_key['scheme']
423    >>> ed25519_metadata = \
424    format_keyval_to_metadata(keytype, scheme, key_val, private=True)
425    >>> securesystemslib.formats.KEY_SCHEMA.matches(ed25519_metadata)
426    True
427
428  <Arguments>
429    key_type:
430      The 'rsa' or 'ed25519' strings.
431
432    scheme:
433      The signature scheme used by the key.
434
435    key_value:
436      A dictionary containing a private and public keys.
437      'key_value' is of the form:
438
439      {'public': '...',
440       'private': '...'}},
441
442      conformant to 'securesystemslib.formats.KEYVAL_SCHEMA'.
443
444    private:
445      Indicates if the private key should be included in the dictionary
446      returned.
447
448  <Exceptions>
449    securesystemslib.exceptions.FormatError, if 'key_value' does not conform to
450    'securesystemslib.formats.KEYVAL_SCHEMA', or if the private key is not
451    present in 'key_value' if requested by the caller via 'private'.
452
453  <Side Effects>
454    None.
455
456  <Returns>
457    A 'securesystemslib.formats.KEY_SCHEMA' dictionary.
458  """
459
460  # Does 'keytype' have the correct format?
461  # This check will ensure 'keytype' has the appropriate number
462  # of objects and object types, and that all dict keys are properly named.
463  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
464  securesystemslib.formats.KEYTYPE_SCHEMA.check_match(keytype)
465
466  # Does 'scheme' have the correct format?
467  securesystemslib.formats.SCHEME_SCHEMA.check_match(scheme)
468
469  # Does 'key_value' have the correct format?
470  securesystemslib.formats.KEYVAL_SCHEMA.check_match(key_value)
471
472  if private is True:
473    # If the caller requests (via the 'private' argument) to include a private
474    # key in the returned dictionary, ensure the private key is actually
475    # present in 'key_val' (a private key is optional for 'KEYVAL_SCHEMA'
476    # dicts).
477    if 'private' not in key_value:
478      raise securesystemslib.exceptions.FormatError('The required private key'
479        ' is missing from: ' + repr(key_value))
480
481    else:
482      return {'keytype': keytype, 'scheme': scheme, 'keyval': key_value}
483
484  else:
485    public_key_value = {'public': key_value['public']}
486
487    return {'keytype': keytype,
488            'scheme': scheme,
489            'keyid_hash_algorithms': securesystemslib.settings.HASH_ALGORITHMS,
490            'keyval': public_key_value}
491
492
493
494
495
496def format_metadata_to_key(key_metadata):
497  """
498  <Purpose>
499    Construct a key dictionary (e.g., securesystemslib.formats.RSAKEY_SCHEMA)
500    according to the keytype of 'key_metadata'.  The dict returned by this
501    function has the exact format as the dict returned by one of the key
502    generations functions, like generate_ed25519_key().  The dict returned
503    has the form:
504
505    {'keytype': keytype,
506     'scheme': scheme,
507     'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
508     'keyval': {'public': '...',
509                'private': '...'}}
510
511    For example, RSA key dictionaries in RSAKEY_SCHEMA format should be used by
512    modules storing a collection of keys, such as with keydb.py.  RSA keys as
513    stored in metadata files use a different format, so this function should be
514    called if an RSA key is extracted from one of these metadata files and need
515    converting.  The key generation functions create an entirely new key and
516    return it in the format appropriate for 'keydb.py'.
517
518    >>> ed25519_key = generate_ed25519_key()
519    >>> key_val = ed25519_key['keyval']
520    >>> keytype = ed25519_key['keytype']
521    >>> scheme = ed25519_key['scheme']
522    >>> ed25519_metadata = \
523    format_keyval_to_metadata(keytype, scheme, key_val, private=True)
524    >>> ed25519_key_2, junk = format_metadata_to_key(ed25519_metadata)
525    >>> securesystemslib.formats.ED25519KEY_SCHEMA.matches(ed25519_key_2)
526    True
527    >>> ed25519_key == ed25519_key_2
528    True
529
530  <Arguments>
531    key_metadata:
532      The key dictionary as stored in Metadata files, conforming to
533      'securesystemslib.formats.KEY_SCHEMA'.  It has the form:
534
535      {'keytype': '...',
536       'scheme': scheme,
537       'keyval': {'public': '...',
538                  'private': '...'}}
539
540  <Exceptions>
541    securesystemslib.exceptions.FormatError, if 'key_metadata' does not conform
542    to 'securesystemslib.formats.KEY_SCHEMA'.
543
544  <Side Effects>
545    None.
546
547  <Returns>
548    In the case of an RSA key, a dictionary conformant to
549    'securesystemslib.formats.RSAKEY_SCHEMA'.
550  """
551
552  # Does 'key_metadata' have the correct format?
553  # This check will ensure 'key_metadata' has the appropriate number
554  # of objects and object types, and that all dict keys are properly named.
555  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
556  securesystemslib.formats.KEY_SCHEMA.check_match(key_metadata)
557
558  # Construct the dictionary to be returned.
559  key_dict = {}
560  keytype = key_metadata['keytype']
561  scheme = key_metadata['scheme']
562  key_value = key_metadata['keyval']
563
564  # Convert 'key_value' to 'securesystemslib.formats.KEY_SCHEMA' and generate
565  # its hash The hash is in hexdigest form.
566  default_keyid = _get_keyid(keytype, scheme, key_value)
567  keyids = set()
568  keyids.add(default_keyid)
569
570  for hash_algorithm in securesystemslib.settings.HASH_ALGORITHMS:
571    keyid = _get_keyid(keytype, scheme, key_value, hash_algorithm)
572    keyids.add(keyid)
573
574  # All the required key values gathered.  Build 'key_dict'.
575  # 'keyid_hash_algorithms'
576  key_dict['keytype'] = keytype
577  key_dict['scheme'] = scheme
578  key_dict['keyid'] = default_keyid
579  key_dict['keyid_hash_algorithms'] = securesystemslib.settings.HASH_ALGORITHMS
580  key_dict['keyval'] = key_value
581
582  return key_dict, keyids
583
584
585
586def _get_keyid(keytype, scheme, key_value, hash_algorithm = 'sha256'):
587  """Return the keyid of 'key_value'."""
588
589  # 'keyid' will be generated from an object conformant to KEY_SCHEMA,
590  # which is the format Metadata files (e.g., root.json) store keys.
591  # 'format_keyval_to_metadata()' returns the object needed by _get_keyid().
592  key_meta = format_keyval_to_metadata(keytype, scheme, key_value, private=False)
593
594  # Convert the key to JSON Canonical format, suitable for adding
595  # to digest objects.
596  key_update_data = securesystemslib.formats.encode_canonical(key_meta)
597
598  # Create a digest object and call update(), using the JSON
599  # canonical format of 'rskey_meta' as the update data.
600  digest_object = securesystemslib.hash.digest(hash_algorithm)
601  digest_object.update(key_update_data.encode('utf-8'))
602
603  # 'keyid' becomes the hexadecimal representation of the hash.
604  keyid = digest_object.hexdigest()
605
606  return keyid
607
608
609
610
611
612def create_signature(key_dict, data):
613  """
614  <Purpose>
615    Return a signature dictionary of the form:
616    {'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
617     'sig': '...'}.
618
619    The signing process will use the private key in
620    key_dict['keyval']['private'] and 'data' to generate the signature.
621
622    The following signature schemes are supported:
623
624    'RSASSA-PSS'
625    RFC3447 - RSASSA-PSS
626    http://www.ietf.org/rfc/rfc3447.
627
628    'ed25519'
629    ed25519 - high-speed high security signatures
630    http://ed25519.cr.yp.to/
631
632    Which signature to generate is determined by the key type of 'key_dict'
633    and the available cryptography library specified in 'settings'.
634
635    >>> ed25519_key = generate_ed25519_key()
636    >>> data = 'The quick brown fox jumps over the lazy dog'
637    >>> signature = create_signature(ed25519_key, data)
638    >>> securesystemslib.formats.SIGNATURE_SCHEMA.matches(signature)
639    True
640    >>> len(signature['sig'])
641    128
642    >>> rsa_key = generate_rsa_key(2048)
643    >>> signature = create_signature(rsa_key, data)
644    >>> securesystemslib.formats.SIGNATURE_SCHEMA.matches(signature)
645    True
646    >>> ecdsa_key = generate_ecdsa_key()
647    >>> signature = create_signature(ecdsa_key, data)
648    >>> securesystemslib.formats.SIGNATURE_SCHEMA.matches(signature)
649    True
650
651  <Arguments>
652    key_dict:
653      A dictionary containing the keys.  An example RSA key dict has the
654      form:
655
656      {'keytype': 'rsa',
657       'scheme': 'rsassa-pss-sha256',
658       'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
659       'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...',
660                  'private': '-----BEGIN RSA PRIVATE KEY----- ...'}}
661
662      The public and private keys are strings in PEM format.
663
664    data:
665      Data object used by create_signature() to generate the signature.
666
667  <Exceptions>
668    securesystemslib.exceptions.FormatError, if 'key_dict' is improperly
669    formatted.
670
671    securesystemslib.exceptions.UnsupportedAlgorithmError, if 'key_dict'
672    specifies an unsupported key type or signing scheme.
673
674    TypeError, if 'key_dict' contains an invalid keytype.
675
676  <Side Effects>
677    The cryptography library specified in 'settings' is called to perform the
678    actual signing routine.
679
680  <Returns>
681    A signature dictionary conformant to
682    'securesystemslib_format.SIGNATURE_SCHEMA'.
683  """
684
685  # Does 'key_dict' have the correct format?
686  # This check will ensure 'key_dict' has the appropriate number of objects
687  # and object types, and that all dict keys are properly named.
688  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
689  # The key type of 'key_dict' must be either 'rsa' or 'ed25519'.
690  securesystemslib.formats.ANYKEY_SCHEMA.check_match(key_dict)
691
692  # Signing the 'data' object requires a private key.  'rsassa-pss-sha256',
693  # 'ed25519', and 'ecdsa-sha2-nistp256' are the only signing schemes currently
694  # supported.  RSASSA-PSS keys and signatures can be generated and verified by
695  # pyca_crypto_keys.py, and Ed25519 keys by PyNaCl and PyCA's optimized, pure
696  # python implementation of Ed25519.
697  signature = {}
698  keytype = key_dict['keytype']
699  scheme = key_dict['scheme']
700  public = key_dict['keyval']['public']
701  private = key_dict['keyval']['private']
702  keyid = key_dict['keyid']
703  sig = None
704
705  # Convert 'data' to canonical JSON format so that repeatable signatures are
706  # generated across different platforms and Python key dictionaries.  The
707  # resulting 'data' is a string encoded in UTF-8 and compatible with the input
708  # expected by the cryptography functions called below.
709  data = securesystemslib.formats.encode_canonical(data)
710
711  if keytype == 'rsa':
712    if scheme == 'rsassa-pss-sha256':
713      private = private.replace('\r\n', '\n')
714      sig, scheme = securesystemslib.pyca_crypto_keys.create_rsa_signature(private,
715        data.encode('utf-8'), scheme)
716
717    else:
718      raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported'
719        ' RSA signature scheme specified: ' + repr(scheme))
720
721  elif keytype == 'ed25519':
722    public = binascii.unhexlify(public.encode('utf-8'))
723    private = binascii.unhexlify(private.encode('utf-8'))
724    sig, scheme = securesystemslib.ed25519_keys.create_signature(public,
725      private, data.encode('utf-8'), scheme)
726
727  elif keytype == 'ecdsa-sha2-nistp256':
728    sig, scheme = securesystemslib.ecdsa_keys.create_signature(public, private,
729      data.encode('utf-8'), scheme)
730
731  # 'securesystemslib.formats.ANYKEY_SCHEMA' should have detected invalid key
732  # types.  This is a defensive check against an invalid key type.
733  else: # pragma: no cover
734    raise TypeError('Invalid key type.')
735
736  # Build the signature dictionary to be returned.
737  # The hexadecimal representation of 'sig' is stored in the signature.
738  signature['keyid'] = keyid
739  signature['sig'] = binascii.hexlify(sig).decode()
740
741  return signature
742
743
744
745
746
747def verify_signature(key_dict, signature, data):
748  """
749  <Purpose>
750    Determine whether the private key belonging to 'key_dict' produced
751    'signature'.  verify_signature() will use the public key found in
752    'key_dict', the 'sig' objects contained in 'signature', and 'data' to
753    complete the verification.
754
755    >>> ed25519_key = generate_ed25519_key()
756    >>> data = 'The quick brown fox jumps over the lazy dog'
757    >>> signature = create_signature(ed25519_key, data)
758    >>> verify_signature(ed25519_key, signature, data)
759    True
760    >>> verify_signature(ed25519_key, signature, 'bad_data')
761    False
762    >>> rsa_key = generate_rsa_key()
763    >>> signature = create_signature(rsa_key, data)
764    >>> verify_signature(rsa_key, signature, data)
765    True
766    >>> verify_signature(rsa_key, signature, 'bad_data')
767    False
768    >>> ecdsa_key = generate_ecdsa_key()
769    >>> signature = create_signature(ecdsa_key, data)
770    >>> verify_signature(ecdsa_key, signature, data)
771    True
772    >>> verify_signature(ecdsa_key, signature, 'bad_data')
773    False
774
775  <Arguments>
776    key_dict:
777      A dictionary containing the keys and other identifying information.
778      If 'key_dict' is an RSA key, it has the form:
779
780      {'keytype': 'rsa',
781       'scheme': 'rsassa-pss-sha256',
782       'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
783       'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...',
784                  'private': '-----BEGIN RSA PRIVATE KEY----- ...'}}
785
786      The public and private keys are strings in PEM format.
787
788    signature:
789      The signature dictionary produced by one of the key generation functions.
790      'signature' has the form:
791
792      {'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...',
793       'sig': sig}.
794
795      Conformant to 'securesystemslib.formats.SIGNATURE_SCHEMA'.
796
797    data:
798      Data object used by securesystemslib.rsa_key.create_signature() to
799      generate 'signature'.  'data' is needed here to verify the signature.
800
801  <Exceptions>
802    securesystemslib.exceptions.FormatError, raised if either 'key_dict' or
803    'signature' are improperly formatted.
804
805    securesystemslib.exceptions.UnsupportedAlgorithmError, if 'key_dict' or
806    'signature' specifies an unsupported algorithm.
807
808    securesystemslib.exceptions.CryptoError, if the KEYID in the given
809    'key_dict' does not match the KEYID in 'signature'.
810
811  <Side Effects>
812    The cryptography library specified in 'settings' called to do the actual
813    verification.
814
815  <Returns>
816    Boolean.  True if the signature is valid, False otherwise.
817  """
818
819  # Does 'key_dict' have the correct format?
820  # This check will ensure 'key_dict' has the appropriate number
821  # of objects and object types, and that all dict keys are properly named.
822  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
823  securesystemslib.formats.ANYKEY_SCHEMA.check_match(key_dict)
824
825  # Does 'signature' have the correct format?
826  securesystemslib.formats.SIGNATURE_SCHEMA.check_match(signature)
827
828  # Verify that the KEYID in 'key_dict' matches the KEYID listed in the
829  # 'signature'.
830  if key_dict['keyid'] != signature['keyid']:
831    raise securesystemslib.exceptions.CryptoError('The KEYID ('
832        ' ' + repr(key_dict['keyid']) + ' ) in the given key does not match'
833        ' the KEYID ( ' + repr(signature['keyid']) + ' ) in the signature.')
834
835  else:
836    logger.debug('The KEYIDs of key_dict and the signature match.')
837
838  # Using the public key belonging to 'key_dict'
839  # (i.e., rsakey_dict['keyval']['public']), verify whether 'signature'
840  # was produced by key_dict's corresponding private key
841  # key_dict['keyval']['private'].
842  sig = signature['sig']
843  sig = binascii.unhexlify(sig.encode('utf-8'))
844  public = key_dict['keyval']['public']
845  keytype = key_dict['keytype']
846  scheme = key_dict['scheme']
847  valid_signature = False
848
849  # Convert 'data' to canonical JSON format so that repeatable signatures are
850  # generated across different platforms and Python key dictionaries.  The
851  # resulting 'data' is a string encoded in UTF-8 and compatible with the input
852  # expected by the cryptography functions called below.
853  data = securesystemslib.formats.encode_canonical(data).encode('utf-8')
854
855  if keytype == 'rsa':
856    if scheme == 'rsassa-pss-sha256':
857      valid_signature = securesystemslib.pyca_crypto_keys.verify_rsa_signature(sig,
858        scheme, public, data)
859
860    else:
861      raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported'
862          ' signature scheme is specified: ' + repr(scheme))
863
864  elif keytype == 'ed25519':
865    if scheme == 'ed25519':
866      public = binascii.unhexlify(public.encode('utf-8'))
867      valid_signature = securesystemslib.ed25519_keys.verify_signature(public,
868          scheme, sig, data, use_pynacl=USE_PYNACL)
869
870    else:
871      raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported'
872          ' signature scheme is specified: ' + repr(scheme))
873
874  elif keytype == 'ecdsa-sha2-nistp256':
875    if scheme == 'ecdsa-sha2-nistp256':
876      valid_signature = securesystemslib.ecdsa_keys.verify_signature(public,
877        scheme, sig, data)
878
879    else:
880      raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported'
881          ' signature scheme is specified: ' + repr(scheme))
882
883  # 'securesystemslib.formats.ANYKEY_SCHEMA' should have detected invalid key
884  # types.  This is a defensive check against an invalid key type.
885  else: # pragma: no cover
886    raise TypeError('Unsupported key type.')
887
888  return valid_signature
889
890
891
892
893
894def import_rsakey_from_private_pem(pem, scheme='rsassa-pss-sha256', password=None):
895  """
896  <Purpose>
897    Import the private RSA key stored in 'pem', and generate its public key
898    (which will also be included in the returned rsakey object).  In addition,
899    a keyid identifier for the RSA key is generated.  The object returned
900    conforms to 'securesystemslib.formats.RSAKEY_SCHEMA' and has the form:
901
902    {'keytype': 'rsa',
903     'scheme': 'rsassa-pss-sha256',
904     'keyid': keyid,
905     'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...',
906                'private': '-----BEGIN RSA PRIVATE KEY----- ...'}}
907
908    The private key is a string in PEM format.
909
910    >>> rsa_key = generate_rsa_key()
911    >>> scheme = rsa_key['scheme']
912    >>> private = rsa_key['keyval']['private']
913    >>> passphrase = 'secret'
914    >>> encrypted_pem = create_rsa_encrypted_pem(private, passphrase)
915    >>> rsa_key2 = import_rsakey_from_private_pem(encrypted_pem, scheme, passphrase)
916    >>> securesystemslib.formats.RSAKEY_SCHEMA.matches(rsa_key)
917    True
918    >>> securesystemslib.formats.RSAKEY_SCHEMA.matches(rsa_key2)
919    True
920
921  <Arguments>
922    pem:
923      A string in PEM format.  The private key is extracted and returned in
924      an rsakey object.
925
926    scheme:
927      The signature scheme used by the imported key.
928
929    password: (optional)
930      The password, or passphrase, to decrypt the private part of the RSA key
931      if it is encrypted.  'password' is not used directly as the encryption
932      key, a stronger encryption key is derived from it.
933
934  <Exceptions>
935    securesystemslib.exceptions.FormatError, if the arguments are improperly
936    formatted.
937
938    securesystemslib.exceptions.UnsupportedAlgorithmError, if 'pem' specifies
939    an unsupported key type.
940
941  <Side Effects>
942    None.
943
944  <Returns>
945    A dictionary containing the RSA keys and other identifying information.
946    Conforms to 'securesystemslib.formats.RSAKEY_SCHEMA'.
947  """
948
949  # Does 'pem' have the correct format?
950  # This check will ensure 'pem' conforms to
951  # 'securesystemslib.formats.PEMRSA_SCHEMA'.
952  securesystemslib.formats.PEMRSA_SCHEMA.check_match(pem)
953
954  # Is 'scheme' properly formatted?
955  securesystemslib.formats.RSA_SCHEME_SCHEMA.check_match(scheme)
956
957  if password is not None:
958    securesystemslib.formats.PASSWORD_SCHEMA.check_match(password)
959
960  else:
961    logger.debug('The password/passphrase is unset.  The PEM is expected'
962      ' to be unencrypted.')
963
964  # Begin building the RSA key dictionary.
965  rsakey_dict = {}
966  keytype = 'rsa'
967  public = None
968  private = None
969
970  # Generate the public and private RSA keys.  The pyca/cryptography library
971  # performs the actual crypto operations.
972  public, private = \
973    securesystemslib.pyca_crypto_keys.create_rsa_public_and_private_from_pem(
974    pem, password)
975
976  public =  extract_pem(public, private_pem=False)
977  private = extract_pem(private, private_pem=True)
978
979  # Generate the keyid of the RSA key.  'key_value' corresponds to the
980  # 'keyval' entry of the 'RSAKEY_SCHEMA' dictionary.  The private key
981  # information is not included in the generation of the 'keyid' identifier.
982  # Convert any '\r\n' (e.g., Windows) newline characters to '\n' so that a
983  # consistent keyid is generated.
984  key_value = {'public': public.replace('\r\n', '\n'),
985               'private': ''}
986  keyid = _get_keyid(keytype, scheme, key_value)
987
988  # Build the 'rsakey_dict' dictionary.  Update 'key_value' with the RSA
989  # private key prior to adding 'key_value' to 'rsakey_dict'.
990  key_value['private'] = private
991
992  rsakey_dict['keytype'] = keytype
993  rsakey_dict['scheme'] = scheme
994  rsakey_dict['keyid'] = keyid
995  rsakey_dict['keyval'] = key_value
996
997  return rsakey_dict
998
999
1000
1001
1002
1003def import_rsakey_from_public_pem(pem, scheme='rsassa-pss-sha256'):
1004  """
1005  <Purpose>
1006    Generate an RSA key object from 'pem'.  In addition, a keyid identifier for
1007    the RSA key is generated.  The object returned conforms to
1008    'securesystemslib.formats.RSAKEY_SCHEMA' and has the form:
1009
1010    {'keytype': 'rsa',
1011     'keyid': keyid,
1012     'keyval': {'public': '-----BEGIN PUBLIC KEY----- ...',
1013                'private': ''}}
1014
1015    The public portion of the RSA key is a string in PEM format.
1016
1017    >>> rsa_key = generate_rsa_key()
1018    >>> public = rsa_key['keyval']['public']
1019    >>> rsa_key['keyval']['private'] = ''
1020    >>> rsa_key2 = import_rsakey_from_public_pem(public)
1021    >>> securesystemslib.formats.RSAKEY_SCHEMA.matches(rsa_key)
1022    True
1023    >>> securesystemslib.formats.RSAKEY_SCHEMA.matches(rsa_key2)
1024    True
1025
1026  <Arguments>
1027    pem:
1028      A string in PEM format (it should contain a public RSA key).
1029
1030  <Exceptions>
1031    securesystemslib.exceptions.FormatError, if 'pem' is improperly formatted.
1032
1033  <Side Effects>
1034    Only the public portion of the PEM is extracted.  Leading or trailing
1035    whitespace is not included in the PEM string stored in the rsakey object
1036    returned.
1037
1038  <Returns>
1039    A dictionary containing the RSA keys and other identifying information.
1040    Conforms to 'securesystemslib.formats.RSAKEY_SCHEMA'.
1041  """
1042
1043  # Does 'pem' have the correct format?
1044  # This check will ensure arguments has the appropriate number
1045  # of objects and object types, and that all dict keys are properly named.
1046  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
1047  securesystemslib.formats.PEMRSA_SCHEMA.check_match(pem)
1048
1049  # Does 'scheme' have the correct format?
1050  securesystemslib.formats.RSA_SCHEME_SCHEMA.check_match(scheme)
1051
1052  # Ensure the PEM string has a public header and footer.  Although a simple
1053  # validation of 'pem' is performed here, a fully valid PEM string is needed
1054  # later to successfully verify signatures.  Performing stricter validation of
1055  # PEMs are left to the external libraries that use 'pem'.
1056
1057  if is_pem_public(pem):
1058    public_pem = extract_pem(pem, private_pem=False)
1059
1060  else:
1061    raise securesystemslib.exceptions.FormatError('Invalid public'
1062        ' pem: ' + repr(pem))
1063
1064  # Begin building the RSA key dictionary.
1065  rsakey_dict = {}
1066  keytype = 'rsa'
1067
1068  # Generate the keyid of the RSA key.  'key_value' corresponds to the
1069  # 'keyval' entry of the 'RSAKEY_SCHEMA' dictionary.  The private key
1070  # information is not included in the generation of the 'keyid' identifier.
1071  # Convert any '\r\n' (e.g., Windows) newline characters to '\n' so that a
1072  # consistent keyid is generated.
1073  key_value = {'public': public_pem.replace('\r\n', '\n'),
1074               'private': ''}
1075  keyid = _get_keyid(keytype, scheme, key_value)
1076
1077  rsakey_dict['keytype'] = keytype
1078  rsakey_dict['scheme'] = scheme
1079  rsakey_dict['keyid'] = keyid
1080  rsakey_dict['keyval'] = key_value
1081
1082  # Add "keyid_hash_algorithms" so that equal RSA keys with different keyids
1083  # can be associated using supported keyid_hash_algorithms.
1084  rsakey_dict['keyid_hash_algorithms'] = \
1085      securesystemslib.settings.HASH_ALGORITHMS
1086
1087  return rsakey_dict
1088
1089
1090
1091
1092
1093def import_rsakey_from_pem(pem, scheme='rsassa-pss-sha256'):
1094  """
1095  <Purpose>
1096    Import either a public or private PEM.  In contrast to the other explicit
1097    import functions (import_rsakey_from_public_pem and
1098    import_rsakey_from_private_pem), this function is useful for when it is not
1099    known whether 'pem' is private or public.
1100
1101  <Arguments>
1102    pem:
1103      A string in PEM format.
1104
1105    scheme:
1106      The signature scheme used by the imported key.
1107
1108  <Exceptions>
1109    securesystemslib.exceptions.FormatError, if 'pem' is improperly formatted.
1110
1111  <Side Effects>
1112    None.
1113
1114  <Returns>
1115    A dictionary containing the RSA keys and other identifying information.
1116    Conforms to 'securesystemslib.formats.RSAKEY_SCHEMA'.
1117  """
1118
1119  # Does 'pem' have the correct format?
1120  # This check will ensure arguments has the appropriate number
1121  # of objects and object types, and that all dict keys are properly named.
1122  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
1123  securesystemslib.formats.PEMRSA_SCHEMA.check_match(pem)
1124
1125  # Is 'scheme' properly formatted?
1126  securesystemslib.formats.RSA_SCHEME_SCHEMA.check_match(scheme)
1127
1128  public_pem = ''
1129  private_pem = ''
1130
1131  # Ensure the PEM string has a public or private header and footer.  Although
1132  # a simple validation of 'pem' is performed here, a fully valid PEM string is
1133  # needed later to successfully verify signatures.  Performing stricter
1134  # validation of PEMs are left to the external libraries that use 'pem'.
1135  if is_pem_public(pem):
1136    public_pem = extract_pem(pem, private_pem=False)
1137
1138  elif is_pem_private(pem):
1139    # Return an rsakey object (RSAKEY_SCHEMA) with the private key included.
1140    return import_rsakey_from_private_pem(pem, password=None)
1141
1142  else:
1143    raise securesystemslib.exceptions.FormatError('PEM contains neither a'
1144      ' public nor private key: ' + repr(pem))
1145
1146  # Begin building the RSA key dictionary.
1147  rsakey_dict = {}
1148  keytype = 'rsa'
1149
1150  # Generate the keyid of the RSA key.  'key_value' corresponds to the 'keyval'
1151  # entry of the 'RSAKEY_SCHEMA' dictionary.  The private key information is
1152  # not included in the generation of the 'keyid' identifier.  If a PEM is
1153  # found to contain a private key, the generated rsakey object should be
1154  # returned above.  The following key object is for the case of a PEM with
1155  # only a public key.  Convert any '\r\n' (e.g., Windows) newline characters
1156  # to '\n' so that a consistent keyid is generated.
1157  key_value = {'public': public_pem.replace('\r\n', '\n'),
1158               'private': ''}
1159  keyid = _get_keyid(keytype, scheme, key_value)
1160
1161  rsakey_dict['keytype'] = keytype
1162  rsakey_dict['scheme'] = scheme
1163  rsakey_dict['keyid'] = keyid
1164  rsakey_dict['keyval'] = key_value
1165
1166  # Add "keyid_hash_algorithms" so that equal RSA keys with
1167  # different keyids can be associated using supported keyid_hash_algorithms.
1168  rsakey_dict['keyid_hash_algorithms'] = \
1169      securesystemslib.settings.HASH_ALGORITHMS
1170
1171  return rsakey_dict
1172
1173
1174
1175
1176def extract_pem(pem, private_pem=False):
1177  """
1178  <Purpose>
1179    Extract only the portion of the pem that includes the header and footer,
1180    with any leading and trailing characters removed.  The string returned has
1181    the following form:
1182
1183    '-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----'
1184
1185    or
1186
1187    '-----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----'
1188
1189    Note: This function assumes "pem" is a valid pem in the following format:
1190    pem header + key material + key footer.  Crypto libraries (e.g., pyca's
1191    cryptography) that parse the pem returned by this function are expected to
1192    fully validate the pem.
1193
1194  <Arguments>
1195    pem:
1196      A string in PEM format.
1197
1198    private_pem:
1199      Boolean that indicates whether 'pem' is a private PEM.  Private PEMs
1200      are not shown in exception messages.
1201
1202  <Exceptions>
1203    securesystemslib.exceptions.FormatError, if 'pem' is improperly formatted.
1204
1205  <Side Effects>
1206    Only the public and private portion of the PEM is extracted.  Leading or
1207    trailing whitespace is not included in the returned PEM string.
1208
1209  <Returns>
1210    A PEM string (excluding leading and trailing newline characters).
1211    That is: pem header + key material + pem footer.
1212
1213  """
1214
1215  if private_pem:
1216    pem_header = '-----BEGIN RSA PRIVATE KEY-----'
1217    pem_footer = '-----END RSA PRIVATE KEY-----'
1218
1219  else:
1220    pem_header = '-----BEGIN PUBLIC KEY-----'
1221    pem_footer = '-----END PUBLIC KEY-----'
1222
1223  header_start = 0
1224  footer_start = 0
1225
1226  # Raise error message if the expected header or footer is not found in 'pem'.
1227  try:
1228    header_start = pem.index(pem_header)
1229
1230  except ValueError:
1231    # Be careful not to print private key material in exception message.
1232    if not private_pem:
1233      raise securesystemslib.exceptions.FormatError('Required PEM'
1234        ' header ' + repr(pem_header) + '\n not found in PEM'
1235        ' string: ' + repr(pem))
1236
1237    else:
1238      raise securesystemslib.exceptions.FormatError('Required PEM'
1239        ' header ' + repr(pem_header) + '\n not found in private PEM string.')
1240
1241  try:
1242    # Search for 'pem_footer' after the PEM header.
1243    footer_start = pem.index(pem_footer, header_start + len(pem_header))
1244
1245  except ValueError:
1246    # Be careful not to print private key material in exception message.
1247    if not private_pem:
1248      raise securesystemslib.exceptions.FormatError('Required PEM'
1249        ' footer ' + repr(pem_footer) + '\n not found in PEM'
1250        ' string ' + repr(pem))
1251
1252    else:
1253      raise securesystemslib.exceptions.FormatError('Required PEM'
1254        ' footer ' + repr(pem_footer) + '\n not found in private PEM string.')
1255
1256  # Extract only the public portion of 'pem'.  Leading or trailing whitespace
1257  # is excluded.
1258  pem = pem[header_start:footer_start + len(pem_footer)]
1259
1260  return pem
1261
1262
1263
1264
1265
1266def encrypt_key(key_object, password):
1267  """
1268  <Purpose>
1269    Return a string containing 'key_object' in encrypted form. Encrypted
1270    strings may be safely saved to a file.  The corresponding decrypt_key()
1271    function can be applied to the encrypted string to restore the original key
1272    object.  'key_object' is a key (e.g., RSAKEY_SCHEMA, ED25519KEY_SCHEMA).
1273    This function relies on the pyca_crypto_keys.py module to perform the
1274    actual encryption.
1275
1276    Encrypted keys use AES-256-CTR-Mode, and passwords are strengthened with
1277    PBKDF2-HMAC-SHA256 (100K iterations by default, but may be overriden in
1278    'securesystemslib.settings.PBKDF2_ITERATIONS' by the user).
1279
1280    http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
1281    http://en.wikipedia.org/wiki/CTR_mode#Counter_.28CTR.29
1282    https://en.wikipedia.org/wiki/PBKDF2
1283
1284    >>> ed25519_key = generate_ed25519_key()
1285    >>> password = 'secret'
1286    >>> encrypted_key = encrypt_key(ed25519_key, password).encode('utf-8')
1287    >>> securesystemslib.formats.ENCRYPTEDKEY_SCHEMA.matches(encrypted_key)
1288    True
1289
1290  <Arguments>
1291    key_object:
1292      A key (containing also the private key portion) of the form
1293      'securesystemslib.formats.ANYKEY_SCHEMA'
1294
1295    password:
1296      The password, or passphrase, to encrypt the private part of the RSA
1297      key.  'password' is not used directly as the encryption key, a stronger
1298      encryption key is derived from it.
1299
1300  <Exceptions>
1301    securesystemslib.exceptions.FormatError, if the arguments are improperly
1302    formatted.
1303
1304    securesystemslib.exceptions.CryptoError, if 'key_object' cannot be
1305    encrypted.
1306
1307  <Side Effects>
1308    None.
1309
1310  <Returns>
1311    An encrypted string of the form:
1312    'securesystemslib.formats.ENCRYPTEDKEY_SCHEMA'.
1313  """
1314
1315  # Does 'key_object' have the correct format?
1316  # This check will ensure 'key_object' has the appropriate number
1317  # of objects and object types, and that all dict keys are properly named.
1318  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
1319  securesystemslib.formats.ANYKEY_SCHEMA.check_match(key_object)
1320
1321  # Does 'password' have the correct format?
1322  securesystemslib.formats.PASSWORD_SCHEMA.check_match(password)
1323
1324  # Encrypted string of 'key_object'.  The encrypted string may be safely saved
1325  # to a file and stored offline.
1326  encrypted_key = None
1327
1328  # Generate an encrypted string of 'key_object' using AES-256-CTR-Mode, where
1329  # 'password' is strengthened with PBKDF2-HMAC-SHA256.
1330  encrypted_key = securesystemslib.pyca_crypto_keys.encrypt_key(key_object, password)
1331
1332  return encrypted_key
1333
1334
1335
1336
1337
1338def decrypt_key(encrypted_key, passphrase):
1339  """
1340  <Purpose>
1341    Return a string containing 'encrypted_key' in non-encrypted form.  The
1342    decrypt_key() function can be applied to the encrypted string to restore
1343    the original key object, a key (e.g., RSAKEY_SCHEMA, ED25519KEY_SCHEMA).
1344    This function calls pyca_crypto_keys.py to perform the actual decryption.
1345
1346    Encrypted keys use AES-256-CTR-Mode and passwords are strengthened with
1347    PBKDF2-HMAC-SHA256 (100K iterations be default, but may be overriden in
1348    'settings.py' by the user).
1349
1350    http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
1351    http://en.wikipedia.org/wiki/CTR_mode#Counter_.28CTR.29
1352    https://en.wikipedia.org/wiki/PBKDF2
1353
1354    >>> ed25519_key = generate_ed25519_key()
1355    >>> password = 'secret'
1356    >>> encrypted_key = encrypt_key(ed25519_key, password)
1357    >>> decrypted_key = decrypt_key(encrypted_key.encode('utf-8'), password)
1358    >>> securesystemslib.formats.ANYKEY_SCHEMA.matches(decrypted_key)
1359    True
1360    >>> decrypted_key == ed25519_key
1361    True
1362
1363  <Arguments>
1364    encrypted_key:
1365      An encrypted key (additional data is also included, such as salt, number
1366      of password iterations used for the derived encryption key, etc) of the
1367      form 'securesystemslib.formats.ENCRYPTEDKEY_SCHEMA'.  'encrypted_key'
1368      should have been generated with encrypt_key().
1369
1370    password:
1371      The password, or passphrase, to decrypt 'encrypted_key'.  'password' is
1372      not used directly as the encryption key, a stronger encryption key is
1373      derived from it.  The supported general-purpose module takes care of
1374      re-deriving the encryption key.
1375
1376  <Exceptions>
1377    securesystemslib.exceptions.FormatError, if the arguments are improperly
1378    formatted.
1379
1380    securesystemslib.exceptions.CryptoError, if 'encrypted_key' cannot be
1381    decrypted.
1382
1383  <Side Effects>
1384    None.
1385
1386  <Returns>
1387    A key object of the form: 'securesystemslib.formats.ANYKEY_SCHEMA' (e.g.,
1388    RSAKEY_SCHEMA, ED25519KEY_SCHEMA).
1389  """
1390
1391  # Does 'encrypted_key' have the correct format?
1392  # This check ensures 'encrypted_key' has the appropriate number
1393  # of objects and object types, and that all dict keys are properly named.
1394  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
1395  securesystemslib.formats.ENCRYPTEDKEY_SCHEMA.check_match(encrypted_key)
1396
1397  # Does 'passphrase' have the correct format?
1398  securesystemslib.formats.PASSWORD_SCHEMA.check_match(passphrase)
1399
1400  # Store and return the decrypted key object.
1401  key_object = None
1402
1403  # Decrypt 'encrypted_key' so that the original key object is restored.
1404  # encrypt_key() generates an encrypted string of the key object using
1405  # AES-256-CTR-Mode, where 'password' is strengthened with PBKDF2-HMAC-SHA256.
1406  key_object = \
1407    securesystemslib.pyca_crypto_keys.decrypt_key(encrypted_key, passphrase)
1408
1409  # The corresponding encrypt_key() encrypts and stores key objects in
1410  # non-metadata format (i.e., original format of key object argument to
1411  # encrypt_key()) prior to returning.
1412
1413  return key_object
1414
1415
1416
1417
1418
1419def create_rsa_encrypted_pem(private_key, passphrase):
1420  """
1421  <Purpose>
1422    Return a string in PEM format (TraditionalOpenSSL), where the private part
1423    of the RSA key is encrypted using the best available encryption for a given
1424    key's backend. This is a curated (by cryptography.io) encryption choice and
1425    the algorithm may change over time.
1426
1427    c.f. cryptography.io/en/latest/hazmat/primitives/asymmetric/serialization/
1428        #cryptography.hazmat.primitives.serialization.BestAvailableEncryption
1429
1430  >>> rsa_key = generate_rsa_key()
1431  >>> private = rsa_key['keyval']['private']
1432  >>> passphrase = 'secret'
1433  >>> encrypted_pem = create_rsa_encrypted_pem(private, passphrase)
1434  >>> securesystemslib.formats.PEMRSA_SCHEMA.matches(encrypted_pem)
1435  True
1436
1437  <Arguments>
1438    private_key:
1439      The private key string in PEM format.
1440
1441    passphrase:
1442      The passphrase, or password, to encrypt the private part of the RSA key.
1443      'passphrase' is not used directly as the encryption key, a stronger
1444      encryption key is derived from it.
1445
1446  <Exceptions>
1447    securesystemslib.exceptions.FormatError, if the arguments are improperly
1448    formatted.
1449
1450    securesystemslib.exceptions.CryptoError, if an RSA key in encrypted PEM
1451    format cannot be created.
1452
1453    TypeError, 'private_key' is unset.
1454
1455  <Side Effects>
1456    None.
1457
1458  <Returns>
1459    A string in PEM format, where the private RSA key is encrypted.
1460    Conforms to 'securesystemslib.formats.PEMRSA_SCHEMA'.
1461  """
1462
1463  # Does 'private_key' have the correct format?
1464  # This check will ensure 'private_key' has the appropriate number
1465  # of objects and object types, and that all dict keys are properly named.
1466  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
1467  securesystemslib.formats.PEMRSA_SCHEMA.check_match(private_key)
1468
1469  # Does 'passphrase' have the correct format?
1470  securesystemslib.formats.PASSWORD_SCHEMA.check_match(passphrase)
1471
1472  encrypted_pem = None
1473
1474  # Generate the public and private RSA keys. A 2048-bit minimum is enforced by
1475  # create_rsa_encrypted_pem() via a
1476  # securesystemslib.formats.RSAKEYBITS_SCHEMA.check_match().
1477  encrypted_pem = securesystemslib.pyca_crypto_keys.create_rsa_encrypted_pem(
1478      private_key, passphrase)
1479
1480  return encrypted_pem
1481
1482
1483
1484
1485def is_pem_public(pem):
1486  """
1487  <Purpose>
1488    Checks if a passed PEM formatted string is a PUBLIC key, by looking for the
1489    following pattern:
1490
1491    '-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----'
1492
1493    >>> rsa_key = generate_rsa_key()
1494    >>> public = rsa_key['keyval']['public']
1495    >>> private = rsa_key['keyval']['private']
1496    >>> is_pem_public(public)
1497    True
1498    >>> is_pem_public(private)
1499    False
1500
1501  <Arguments>
1502    pem:
1503      A string in PEM format.
1504
1505  <Exceptions>
1506    securesystemslib.exceptions.FormatError, if 'pem' is improperly formatted.
1507
1508  <Side Effects>
1509    None
1510
1511  <Returns>
1512    True if 'pem' is public and false otherwise.
1513  """
1514
1515  # Do the arguments have the correct format?
1516  # This check will ensure arguments have the appropriate number
1517  # of objects and object types, and that all dict keys are properly named.
1518  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
1519  securesystemslib.formats.PEMRSA_SCHEMA.check_match(pem)
1520
1521  pem_header = '-----BEGIN PUBLIC KEY-----'
1522  pem_footer = '-----END PUBLIC KEY-----'
1523
1524  try:
1525    header_start = pem.index(pem_header)
1526    pem.index(pem_footer, header_start + len(pem_header))
1527
1528  except ValueError:
1529    return False
1530
1531  return True
1532
1533
1534
1535
1536def is_pem_private(pem, keytype='rsa'):
1537  """
1538  <Purpose>
1539    Checks if a passed PEM formatted string is a PRIVATE key, by looking for
1540    the following patterns:
1541
1542    '-----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----'
1543    '-----BEGIN EC PRIVATE KEY----- ... -----END EC PRIVATE KEY-----'
1544
1545    >>> rsa_key = generate_rsa_key()
1546    >>> private = rsa_key['keyval']['private']
1547    >>> public = rsa_key['keyval']['public']
1548    >>> is_pem_private(private)
1549    True
1550    >>> is_pem_private(public)
1551    False
1552
1553  <Arguments>
1554    pem:
1555      A string in PEM format.
1556
1557  <Exceptions>
1558    securesystemslib.exceptions.FormatError, if any of the arguments are
1559    improperly formatted.
1560
1561  <Side Effects>
1562    None
1563
1564  <Returns>
1565    True if 'pem' is private and false otherwise.
1566  """
1567
1568  # Do the arguments have the correct format?
1569  # This check will ensure arguments have the appropriate number
1570  # of objects and object types, and that all dict keys are properly named.
1571  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
1572  securesystemslib.formats.PEMRSA_SCHEMA.check_match(pem)
1573  securesystemslib.formats.NAME_SCHEMA.check_match(keytype)
1574
1575  if keytype == 'rsa':
1576    pem_header = '-----BEGIN RSA PRIVATE KEY-----'
1577    pem_footer = '-----END RSA PRIVATE KEY-----'
1578
1579  elif keytype == 'ec':
1580    pem_header = '-----BEGIN EC PRIVATE KEY-----'
1581    pem_footer = '-----END EC PRIVATE KEY-----'
1582
1583  else:
1584    raise securesystemslib.exceptions.FormatError('Unsupported key'
1585      ' type: ' + repr(keytype) + '.  Supported keytypes: ["rsa", "ec"]')
1586
1587  try:
1588    header_start = pem.index(pem_header)
1589    pem.index(pem_footer, header_start + len(pem_header))
1590
1591  except ValueError:
1592    return False
1593
1594  return True
1595
1596
1597
1598
1599
1600def import_ed25519key_from_private_json(json_str, password=None):
1601  if password is not None:
1602    # This check will not fail, because a mal-formatted passed password fails
1603    # above and an entered password will always be a string (see get_password)
1604    # However, we include it in case PASSWORD_SCHEMA or get_password changes.
1605    securesystemslib.formats.PASSWORD_SCHEMA.check_match(password)
1606
1607    # Decrypt the loaded key file, calling the 'cryptography' library to
1608    # generate the derived encryption key from 'password'.  Raise
1609    # 'securesystemslib.exceptions.CryptoError' if the decryption fails.
1610    key_object = securesystemslib.keys.\
1611                 decrypt_key(json_str.decode('utf-8'), password)
1612
1613  else:
1614    logger.debug('No password was given. Attempting to import an'
1615        ' unencrypted file.')
1616    try:
1617      key_object = \
1618               securesystemslib.util.load_json_string(json_str.decode('utf-8'))
1619    # If the JSON could not be decoded, it is very likely, but not necessarily,
1620    # due to a non-empty password.
1621    except securesystemslib.exceptions.Error:
1622      raise securesystemslib.exceptions\
1623            .CryptoError('Malformed Ed25519 key JSON, '
1624                         'possibly due to encryption, '
1625                         'but no password provided?')
1626
1627  # Raise an exception if an unexpected key type is imported.
1628  if key_object['keytype'] != 'ed25519':
1629    message = 'Invalid key type loaded: ' + repr(key_object['keytype'])
1630    raise securesystemslib.exceptions.FormatError(message)
1631
1632  # Add "keyid_hash_algorithms" so that equal ed25519 keys with
1633  # different keyids can be associated using supported keyid_hash_algorithms.
1634  key_object['keyid_hash_algorithms'] = \
1635      securesystemslib.settings.HASH_ALGORITHMS
1636
1637  return key_object
1638
1639
1640
1641
1642
1643def import_ecdsakey_from_private_pem(pem, scheme='ecdsa-sha2-nistp256', password=None):
1644  """
1645  <Purpose>
1646    Import the private ECDSA key stored in 'pem', and generate its public key
1647    (which will also be included in the returned ECDSA key object).  In addition,
1648    a keyid identifier for the ECDSA key is generated.  The object returned
1649    conforms to:
1650
1651    {'keytype': 'ecdsa-sha2-nistp256',
1652     'scheme': 'ecdsa-sha2-nistp256',
1653     'keyid': keyid,
1654     'keyval': {'public': '-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----',
1655                'private': '-----BEGIN EC PRIVATE KEY----- ... -----END EC PRIVATE KEY-----'}}
1656
1657    The private key is a string in PEM format.
1658
1659    >>> ecdsa_key = generate_ecdsa_key()
1660    >>> private_pem = ecdsa_key['keyval']['private']
1661    >>> ecdsa_key = import_ecdsakey_from_private_pem(private_pem)
1662    >>> securesystemslib.formats.ECDSAKEY_SCHEMA.matches(ecdsa_key)
1663    True
1664
1665  <Arguments>
1666    pem:
1667      A string in PEM format.  The private key is extracted and returned in
1668      an ecdsakey object.
1669
1670    scheme:
1671      The signature scheme used by the imported key.
1672
1673    password: (optional)
1674      The password, or passphrase, to decrypt the private part of the ECDSA
1675      key if it is encrypted.  'password' is not used directly as the encryption
1676      key, a stronger encryption key is derived from it.
1677
1678  <Exceptions>
1679    securesystemslib.exceptions.FormatError, if the arguments are improperly
1680    formatted.
1681
1682    securesystemslib.exceptions.UnsupportedAlgorithmError, if 'pem' specifies
1683    an unsupported key type.
1684
1685  <Side Effects>
1686    None.
1687
1688  <Returns>
1689    A dictionary containing the ECDSA keys and other identifying information.
1690    Conforms to 'securesystemslib.formats.ECDSAKEY_SCHEMA'.
1691  """
1692
1693  # Does 'pem' have the correct format?
1694  # This check will ensure 'pem' conforms to
1695  # 'securesystemslib.formats.ECDSARSA_SCHEMA'.
1696  securesystemslib.formats.PEMECDSA_SCHEMA.check_match(pem)
1697
1698  # Is 'scheme' properly formatted?
1699  securesystemslib.formats.ECDSA_SCHEME_SCHEMA.check_match(scheme)
1700
1701  if password is not None:
1702    securesystemslib.formats.PASSWORD_SCHEMA.check_match(password)
1703
1704  else:
1705    logger.debug('The password/passphrase is unset.  The PEM is expected'
1706      ' to be unencrypted.')
1707
1708  # Begin building the ECDSA key dictionary.
1709  ecdsakey_dict = {}
1710  keytype = 'ecdsa-sha2-nistp256'
1711  public = None
1712  private = None
1713
1714  public, private = \
1715      securesystemslib.ecdsa_keys.create_ecdsa_public_and_private_from_pem(pem,
1716      password)
1717
1718  # Generate the keyid of the ECDSA key.  'key_value' corresponds to the
1719  # 'keyval' entry of the 'ECDSAKEY_SCHEMA' dictionary.  The private key
1720  # information is not included in the generation of the 'keyid' identifier.
1721  # Convert any '\r\n' (e.g., Windows) newline characters to '\n' so that a
1722  # consistent keyid is generated.
1723  key_value = {'public': public.replace('\r\n', '\n'),
1724               'private': ''}
1725  keyid = _get_keyid(keytype, scheme, key_value)
1726
1727  # Build the 'ecdsakey_dict' dictionary.  Update 'key_value' with the ECDSA
1728  # private key prior to adding 'key_value' to 'ecdsakey_dict'.
1729  key_value['private'] = private
1730
1731  ecdsakey_dict['keytype'] = keytype
1732  ecdsakey_dict['scheme'] = scheme
1733  ecdsakey_dict['keyid'] = keyid
1734  ecdsakey_dict['keyval'] = key_value
1735
1736  # Add "keyid_hash_algorithms" so equal ECDSA keys with
1737  # different keyids can be associated using supported keyid_hash_algorithms
1738  ecdsakey_dict['keyid_hash_algorithms'] = \
1739    securesystemslib.settings.HASH_ALGORITHMS
1740
1741  return ecdsakey_dict
1742
1743
1744
1745
1746
1747def import_ecdsakey_from_public_pem(pem, scheme='ecdsa-sha2-nistp256'):
1748  """
1749  <Purpose>
1750    Generate an ECDSA key object from 'pem'.  In addition, a keyid identifier
1751    for the ECDSA key is generated.  The object returned conforms to
1752    'securesystemslib.formats.ECDSAKEY_SCHEMA' and has the form:
1753
1754    {'keytype': 'ecdsa-sha2-nistp256',
1755     'scheme': 'ecdsa-sha2-nistp256',
1756     'keyid': keyid,
1757     'keyval': {'public': '-----BEGIN PUBLIC KEY----- ...',
1758                'private': ''}}
1759
1760    The public portion of the ECDSA key is a string in PEM format.
1761
1762    >>> ecdsa_key = generate_ecdsa_key()
1763    >>> public = ecdsa_key['keyval']['public']
1764    >>> ecdsa_key['keyval']['private'] = ''
1765    >>> scheme = ecdsa_key['scheme']
1766    >>> ecdsa_key2 = import_ecdsakey_from_public_pem(public, scheme)
1767    >>> securesystemslib.formats.ECDSAKEY_SCHEMA.matches(ecdsa_key)
1768    True
1769    >>> securesystemslib.formats.ECDSAKEY_SCHEMA.matches(ecdsa_key2)
1770    True
1771
1772  <Arguments>
1773    pem:
1774      A string in PEM format (it should contain a public ECDSA key).
1775
1776    scheme:
1777      The signature scheme used by the imported key.
1778
1779  <Exceptions>
1780    securesystemslib.exceptions.FormatError, if 'pem' is improperly formatted.
1781
1782  <Side Effects>
1783    Only the public portion of the PEM is extracted.  Leading or trailing
1784    whitespace is not included in the PEM string stored in the rsakey object
1785    returned.
1786
1787  <Returns>
1788    A dictionary containing the ECDSA keys and other identifying information.
1789    Conforms to 'securesystemslib.formats.ECDSAKEY_SCHEMA'.
1790  """
1791
1792  # Does 'pem' have the correct format?
1793  # This check will ensure arguments has the appropriate number
1794  # of objects and object types, and that all dict keys are properly named.
1795  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
1796  securesystemslib.formats.PEMECDSA_SCHEMA.check_match(pem)
1797
1798  # Is 'scheme' properly formatted?
1799  securesystemslib.formats.ECDSA_SCHEME_SCHEMA.check_match(scheme)
1800
1801  # Ensure the PEM string has a public header and footer.  Although a simple
1802  # validation of 'pem' is performed here, a fully valid PEM string is needed
1803  # later to successfully verify signatures.  Performing stricter validation of
1804  # PEMs are left to the external libraries that use 'pem'.
1805
1806  if is_pem_public(pem):
1807    public_pem = extract_pem(pem, private_pem=False)
1808
1809  else:
1810    raise securesystemslib.exceptions.FormatError('Invalid public'
1811        ' pem: ' + repr(pem))
1812
1813  # Begin building the ECDSA key dictionary.
1814  ecdsakey_dict = {}
1815  keytype = 'ecdsa-sha2-nistp256'
1816
1817  # Generate the keyid of the ECDSA key.  'key_value' corresponds to the
1818  # 'keyval' entry of the 'ECDSAKEY_SCHEMA' dictionary.  The private key
1819  # information is not included in the generation of the 'keyid' identifier.
1820  # Convert any '\r\n' (e.g., Windows) newline characters to '\n' so that a
1821  # consistent keyid is generated.
1822  key_value = {'public': public_pem.replace('\r\n', '\n'),
1823               'private': ''}
1824  keyid = _get_keyid(keytype, scheme, key_value)
1825
1826  ecdsakey_dict['keytype'] = keytype
1827  ecdsakey_dict['scheme'] = scheme
1828  ecdsakey_dict['keyid'] = keyid
1829  ecdsakey_dict['keyval'] = key_value
1830
1831  # Add "keyid_hash_algorithms" so that equal ECDSA keys with different keyids
1832  # can be associated using supported keyid_hash_algorithms.
1833  ecdsakey_dict['keyid_hash_algorithms'] = \
1834      securesystemslib.settings.HASH_ALGORITHMS
1835
1836  return ecdsakey_dict
1837
1838
1839
1840
1841
1842def import_ecdsakey_from_pem(pem, scheme='ecdsa-sha2-nistp256'):
1843  """
1844  <Purpose>
1845    Import either a public or private ECDSA PEM.  In contrast to the other
1846    explicit import functions (import_ecdsakey_from_public_pem and
1847    import_ecdsakey_from_private_pem), this function is useful for when it is
1848    not known whether 'pem' is private or public.
1849
1850  <Arguments>
1851    pem:
1852      A string in PEM format.
1853
1854    scheme:
1855      The signature scheme used by the imported key.
1856  <Exceptions>
1857    securesystemslib.exceptions.FormatError, if 'pem' is improperly formatted.
1858
1859  <Side Effects>
1860    None.
1861
1862  <Returns>
1863    A dictionary containing the ECDSA keys and other identifying information.
1864    Conforms to 'securesystemslib.formats.ECDSAKEY_SCHEMA'.
1865  """
1866
1867  # Does 'pem' have the correct format?
1868  # This check will ensure arguments has the appropriate number
1869  # of objects and object types, and that all dict keys are properly named.
1870  # Raise 'securesystemslib.exceptions.FormatError' if the check fails.
1871  securesystemslib.formats.PEMECDSA_SCHEMA.check_match(pem)
1872
1873  # Is 'scheme' properly formatted?
1874  securesystemslib.formats.ECDSA_SCHEME_SCHEMA.check_match(scheme)
1875
1876  public_pem = ''
1877  private_pem = ''
1878
1879  # Ensure the PEM string has a public or private header and footer.  Although
1880  # a simple validation of 'pem' is performed here, a fully valid PEM string is
1881  # needed later to successfully verify signatures.  Performing stricter
1882  # validation of PEMs are left to the external libraries that use 'pem'.
1883  if is_pem_public(pem):
1884    public_pem = extract_pem(pem, private_pem=False)
1885
1886  elif is_pem_private(pem, 'ec'):
1887    # Return an ecdsakey object (ECDSAKEY_SCHEMA) with the private key included.
1888    return import_ecdsakey_from_private_pem(pem, password=None)
1889
1890  else:
1891    raise securesystemslib.exceptions.FormatError('PEM contains neither a public'
1892      ' nor private key: ' + repr(pem))
1893
1894  # Begin building the ECDSA key dictionary.
1895  ecdsakey_dict = {}
1896  keytype = 'ecdsa-sha2-nistp256'
1897
1898  # Generate the keyid of the ECDSA key.  'key_value' corresponds to the
1899  # 'keyval' entry of the 'ECDSAKEY_SCHEMA' dictionary.  The private key
1900  # information is not included in the generation of the 'keyid' identifier.
1901  # If a PEM is found to contain a private key, the generated rsakey object
1902  # should be returned above.  The following key object is for the case of a
1903  # PEM with only a public key.  Convert any '\r\n' (e.g., Windows) newline
1904  # characters to '\n' so that a consistent keyid is generated.
1905  key_value = {'public': public_pem.replace('\r\n', '\n'),
1906               'private': ''}
1907  keyid = _get_keyid(keytype, scheme, key_value)
1908
1909  ecdsakey_dict['keytype'] = keytype
1910  ecdsakey_dict['scheme'] = scheme
1911  ecdsakey_dict['keyid'] = keyid
1912  ecdsakey_dict['keyval'] = key_value
1913
1914  return ecdsakey_dict
1915
1916
1917
1918if __name__ == '__main__':
1919  # The interactive sessions of the documentation strings can
1920  # be tested by running 'keys.py' as a standalone module:
1921  # $ python keys.py
1922  import doctest
1923  doctest.testmod()
1924