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