1# coding: utf-8 2 3""" 4ASN.1 type classes for public and private keys. Exports the following items: 5 6 - DSAPrivateKey() 7 - ECPrivateKey() 8 - EncryptedPrivateKeyInfo() 9 - PrivateKeyInfo() 10 - PublicKeyInfo() 11 - RSAPrivateKey() 12 - RSAPublicKey() 13 14Other type classes are defined that help compose the types listed above. 15""" 16 17from __future__ import unicode_literals, division, absolute_import, print_function 18 19import hashlib 20import math 21 22from ._errors import unwrap, APIException 23from ._types import type_name, byte_cls 24from .algos import _ForceNullParameters, DigestAlgorithm, EncryptionAlgorithm, RSAESOAEPParams, RSASSAPSSParams 25from .core import ( 26 Any, 27 Asn1Value, 28 BitString, 29 Choice, 30 Integer, 31 IntegerOctetString, 32 Null, 33 ObjectIdentifier, 34 OctetBitString, 35 OctetString, 36 ParsableOctetString, 37 ParsableOctetBitString, 38 Sequence, 39 SequenceOf, 40 SetOf, 41) 42from .util import int_from_bytes, int_to_bytes 43 44 45class OtherPrimeInfo(Sequence): 46 """ 47 Source: https://tools.ietf.org/html/rfc3447#page-46 48 """ 49 50 _fields = [ 51 ('prime', Integer), 52 ('exponent', Integer), 53 ('coefficient', Integer), 54 ] 55 56 57class OtherPrimeInfos(SequenceOf): 58 """ 59 Source: https://tools.ietf.org/html/rfc3447#page-46 60 """ 61 62 _child_spec = OtherPrimeInfo 63 64 65class RSAPrivateKeyVersion(Integer): 66 """ 67 Original Name: Version 68 Source: https://tools.ietf.org/html/rfc3447#page-45 69 """ 70 71 _map = { 72 0: 'two-prime', 73 1: 'multi', 74 } 75 76 77class RSAPrivateKey(Sequence): 78 """ 79 Source: https://tools.ietf.org/html/rfc3447#page-45 80 """ 81 82 _fields = [ 83 ('version', RSAPrivateKeyVersion), 84 ('modulus', Integer), 85 ('public_exponent', Integer), 86 ('private_exponent', Integer), 87 ('prime1', Integer), 88 ('prime2', Integer), 89 ('exponent1', Integer), 90 ('exponent2', Integer), 91 ('coefficient', Integer), 92 ('other_prime_infos', OtherPrimeInfos, {'optional': True}) 93 ] 94 95 96class RSAPublicKey(Sequence): 97 """ 98 Source: https://tools.ietf.org/html/rfc3447#page-44 99 """ 100 101 _fields = [ 102 ('modulus', Integer), 103 ('public_exponent', Integer) 104 ] 105 106 107class DSAPrivateKey(Sequence): 108 """ 109 The ASN.1 structure that OpenSSL uses to store a DSA private key that is 110 not part of a PKCS#8 structure. Reversed engineered from english-language 111 description on linked OpenSSL documentation page. 112 113 Original Name: None 114 Source: https://www.openssl.org/docs/apps/dsa.html 115 """ 116 117 _fields = [ 118 ('version', Integer), 119 ('p', Integer), 120 ('q', Integer), 121 ('g', Integer), 122 ('public_key', Integer), 123 ('private_key', Integer), 124 ] 125 126 127class _ECPoint(): 128 """ 129 In both PublicKeyInfo and PrivateKeyInfo, the EC public key is a byte 130 string that is encoded as a bit string. This class adds convenience 131 methods for converting to and from the byte string to a pair of integers 132 that are the X and Y coordinates. 133 """ 134 135 @classmethod 136 def from_coords(cls, x, y): 137 """ 138 Creates an ECPoint object from the X and Y integer coordinates of the 139 point 140 141 :param x: 142 The X coordinate, as an integer 143 144 :param y: 145 The Y coordinate, as an integer 146 147 :return: 148 An ECPoint object 149 """ 150 151 x_bytes = int(math.ceil(math.log(x, 2) / 8.0)) 152 y_bytes = int(math.ceil(math.log(y, 2) / 8.0)) 153 154 num_bytes = max(x_bytes, y_bytes) 155 156 byte_string = b'\x04' 157 byte_string += int_to_bytes(x, width=num_bytes) 158 byte_string += int_to_bytes(y, width=num_bytes) 159 160 return cls(byte_string) 161 162 def to_coords(self): 163 """ 164 Returns the X and Y coordinates for this EC point, as native Python 165 integers 166 167 :return: 168 A 2-element tuple containing integers (X, Y) 169 """ 170 171 data = self.native 172 first_byte = data[0:1] 173 174 # Uncompressed 175 if first_byte == b'\x04': 176 remaining = data[1:] 177 field_len = len(remaining) // 2 178 x = int_from_bytes(remaining[0:field_len]) 179 y = int_from_bytes(remaining[field_len:]) 180 return (x, y) 181 182 if first_byte not in set([b'\x02', b'\x03']): 183 raise ValueError(unwrap( 184 ''' 185 Invalid EC public key - first byte is incorrect 186 ''' 187 )) 188 189 raise ValueError(unwrap( 190 ''' 191 Compressed representations of EC public keys are not supported due 192 to patent US6252960 193 ''' 194 )) 195 196 197class ECPoint(OctetString, _ECPoint): 198 199 pass 200 201 202class ECPointBitString(OctetBitString, _ECPoint): 203 204 pass 205 206 207class SpecifiedECDomainVersion(Integer): 208 """ 209 Source: http://www.secg.org/sec1-v2.pdf page 104 210 """ 211 _map = { 212 1: 'ecdpVer1', 213 2: 'ecdpVer2', 214 3: 'ecdpVer3', 215 } 216 217 218class FieldType(ObjectIdentifier): 219 """ 220 Original Name: None 221 Source: http://www.secg.org/sec1-v2.pdf page 101 222 """ 223 224 _map = { 225 '1.2.840.10045.1.1': 'prime_field', 226 '1.2.840.10045.1.2': 'characteristic_two_field', 227 } 228 229 230class CharacteristicTwoBasis(ObjectIdentifier): 231 """ 232 Original Name: None 233 Source: http://www.secg.org/sec1-v2.pdf page 102 234 """ 235 236 _map = { 237 '1.2.840.10045.1.2.1.1': 'gn_basis', 238 '1.2.840.10045.1.2.1.2': 'tp_basis', 239 '1.2.840.10045.1.2.1.3': 'pp_basis', 240 } 241 242 243class Pentanomial(Sequence): 244 """ 245 Source: http://www.secg.org/sec1-v2.pdf page 102 246 """ 247 248 _fields = [ 249 ('k1', Integer), 250 ('k2', Integer), 251 ('k3', Integer), 252 ] 253 254 255class CharacteristicTwo(Sequence): 256 """ 257 Original Name: Characteristic-two 258 Source: http://www.secg.org/sec1-v2.pdf page 101 259 """ 260 261 _fields = [ 262 ('m', Integer), 263 ('basis', CharacteristicTwoBasis), 264 ('parameters', Any), 265 ] 266 267 _oid_pair = ('basis', 'parameters') 268 _oid_specs = { 269 'gn_basis': Null, 270 'tp_basis': Integer, 271 'pp_basis': Pentanomial, 272 } 273 274 275class FieldID(Sequence): 276 """ 277 Source: http://www.secg.org/sec1-v2.pdf page 100 278 """ 279 280 _fields = [ 281 ('field_type', FieldType), 282 ('parameters', Any), 283 ] 284 285 _oid_pair = ('field_type', 'parameters') 286 _oid_specs = { 287 'prime_field': Integer, 288 'characteristic_two_field': CharacteristicTwo, 289 } 290 291 292class Curve(Sequence): 293 """ 294 Source: http://www.secg.org/sec1-v2.pdf page 104 295 """ 296 297 _fields = [ 298 ('a', OctetString), 299 ('b', OctetString), 300 ('seed', OctetBitString, {'optional': True}), 301 ] 302 303 304class SpecifiedECDomain(Sequence): 305 """ 306 Source: http://www.secg.org/sec1-v2.pdf page 103 307 """ 308 309 _fields = [ 310 ('version', SpecifiedECDomainVersion), 311 ('field_id', FieldID), 312 ('curve', Curve), 313 ('base', ECPoint), 314 ('order', Integer), 315 ('cofactor', Integer, {'optional': True}), 316 ('hash', DigestAlgorithm, {'optional': True}), 317 ] 318 319 320class NamedCurve(ObjectIdentifier): 321 """ 322 Various named curves 323 324 Original Name: None 325 Source: https://tools.ietf.org/html/rfc3279#page-23, 326 https://tools.ietf.org/html/rfc5480#page-5 327 """ 328 329 _map = { 330 # https://tools.ietf.org/html/rfc3279#page-23 331 '1.2.840.10045.3.0.1': 'c2pnb163v1', 332 '1.2.840.10045.3.0.2': 'c2pnb163v2', 333 '1.2.840.10045.3.0.3': 'c2pnb163v3', 334 '1.2.840.10045.3.0.4': 'c2pnb176w1', 335 '1.2.840.10045.3.0.5': 'c2tnb191v1', 336 '1.2.840.10045.3.0.6': 'c2tnb191v2', 337 '1.2.840.10045.3.0.7': 'c2tnb191v3', 338 '1.2.840.10045.3.0.8': 'c2onb191v4', 339 '1.2.840.10045.3.0.9': 'c2onb191v5', 340 '1.2.840.10045.3.0.10': 'c2pnb208w1', 341 '1.2.840.10045.3.0.11': 'c2tnb239v1', 342 '1.2.840.10045.3.0.12': 'c2tnb239v2', 343 '1.2.840.10045.3.0.13': 'c2tnb239v3', 344 '1.2.840.10045.3.0.14': 'c2onb239v4', 345 '1.2.840.10045.3.0.15': 'c2onb239v5', 346 '1.2.840.10045.3.0.16': 'c2pnb272w1', 347 '1.2.840.10045.3.0.17': 'c2pnb304w1', 348 '1.2.840.10045.3.0.18': 'c2tnb359v1', 349 '1.2.840.10045.3.0.19': 'c2pnb368w1', 350 '1.2.840.10045.3.0.20': 'c2tnb431r1', 351 '1.2.840.10045.3.1.2': 'prime192v2', 352 '1.2.840.10045.3.1.3': 'prime192v3', 353 '1.2.840.10045.3.1.4': 'prime239v1', 354 '1.2.840.10045.3.1.5': 'prime239v2', 355 '1.2.840.10045.3.1.6': 'prime239v3', 356 # https://tools.ietf.org/html/rfc5480#page-5 357 # http://www.secg.org/SEC2-Ver-1.0.pdf 358 '1.2.840.10045.3.1.1': 'secp192r1', 359 '1.2.840.10045.3.1.7': 'secp256r1', 360 '1.3.132.0.1': 'sect163k1', 361 '1.3.132.0.2': 'sect163r1', 362 '1.3.132.0.3': 'sect239k1', 363 '1.3.132.0.4': 'sect113r1', 364 '1.3.132.0.5': 'sect113r2', 365 '1.3.132.0.6': 'secp112r1', 366 '1.3.132.0.7': 'secp112r2', 367 '1.3.132.0.8': 'secp160r1', 368 '1.3.132.0.9': 'secp160k1', 369 '1.3.132.0.10': 'secp256k1', 370 '1.3.132.0.15': 'sect163r2', 371 '1.3.132.0.16': 'sect283k1', 372 '1.3.132.0.17': 'sect283r1', 373 '1.3.132.0.22': 'sect131r1', 374 '1.3.132.0.23': 'sect131r2', 375 '1.3.132.0.24': 'sect193r1', 376 '1.3.132.0.25': 'sect193r2', 377 '1.3.132.0.26': 'sect233k1', 378 '1.3.132.0.27': 'sect233r1', 379 '1.3.132.0.28': 'secp128r1', 380 '1.3.132.0.29': 'secp128r2', 381 '1.3.132.0.30': 'secp160r2', 382 '1.3.132.0.31': 'secp192k1', 383 '1.3.132.0.32': 'secp224k1', 384 '1.3.132.0.33': 'secp224r1', 385 '1.3.132.0.34': 'secp384r1', 386 '1.3.132.0.35': 'secp521r1', 387 '1.3.132.0.36': 'sect409k1', 388 '1.3.132.0.37': 'sect409r1', 389 '1.3.132.0.38': 'sect571k1', 390 '1.3.132.0.39': 'sect571r1', 391 # https://tools.ietf.org/html/rfc5639#section-4.1 392 '1.3.36.3.3.2.8.1.1.1': 'brainpoolp160r1', 393 '1.3.36.3.3.2.8.1.1.2': 'brainpoolp160t1', 394 '1.3.36.3.3.2.8.1.1.3': 'brainpoolp192r1', 395 '1.3.36.3.3.2.8.1.1.4': 'brainpoolp192t1', 396 '1.3.36.3.3.2.8.1.1.5': 'brainpoolp224r1', 397 '1.3.36.3.3.2.8.1.1.6': 'brainpoolp224t1', 398 '1.3.36.3.3.2.8.1.1.7': 'brainpoolp256r1', 399 '1.3.36.3.3.2.8.1.1.8': 'brainpoolp256t1', 400 '1.3.36.3.3.2.8.1.1.9': 'brainpoolp320r1', 401 '1.3.36.3.3.2.8.1.1.10': 'brainpoolp320t1', 402 '1.3.36.3.3.2.8.1.1.11': 'brainpoolp384r1', 403 '1.3.36.3.3.2.8.1.1.12': 'brainpoolp384t1', 404 '1.3.36.3.3.2.8.1.1.13': 'brainpoolp512r1', 405 '1.3.36.3.3.2.8.1.1.14': 'brainpoolp512t1', 406 } 407 408 _key_sizes = { 409 # Order values used to compute these sourced from 410 # http://cr.openjdk.java.net/~vinnie/7194075/webrev-3/src/share/classes/sun/security/ec/CurveDB.java.html 411 '1.2.840.10045.3.0.1': 21, 412 '1.2.840.10045.3.0.2': 21, 413 '1.2.840.10045.3.0.3': 21, 414 '1.2.840.10045.3.0.4': 21, 415 '1.2.840.10045.3.0.5': 24, 416 '1.2.840.10045.3.0.6': 24, 417 '1.2.840.10045.3.0.7': 24, 418 '1.2.840.10045.3.0.8': 24, 419 '1.2.840.10045.3.0.9': 24, 420 '1.2.840.10045.3.0.10': 25, 421 '1.2.840.10045.3.0.11': 30, 422 '1.2.840.10045.3.0.12': 30, 423 '1.2.840.10045.3.0.13': 30, 424 '1.2.840.10045.3.0.14': 30, 425 '1.2.840.10045.3.0.15': 30, 426 '1.2.840.10045.3.0.16': 33, 427 '1.2.840.10045.3.0.17': 37, 428 '1.2.840.10045.3.0.18': 45, 429 '1.2.840.10045.3.0.19': 45, 430 '1.2.840.10045.3.0.20': 53, 431 '1.2.840.10045.3.1.2': 24, 432 '1.2.840.10045.3.1.3': 24, 433 '1.2.840.10045.3.1.4': 30, 434 '1.2.840.10045.3.1.5': 30, 435 '1.2.840.10045.3.1.6': 30, 436 # Order values used to compute these sourced from 437 # http://www.secg.org/SEC2-Ver-1.0.pdf 438 # ceil(n.bit_length() / 8) 439 '1.2.840.10045.3.1.1': 24, 440 '1.2.840.10045.3.1.7': 32, 441 '1.3.132.0.1': 21, 442 '1.3.132.0.2': 21, 443 '1.3.132.0.3': 30, 444 '1.3.132.0.4': 15, 445 '1.3.132.0.5': 15, 446 '1.3.132.0.6': 14, 447 '1.3.132.0.7': 14, 448 '1.3.132.0.8': 21, 449 '1.3.132.0.9': 21, 450 '1.3.132.0.10': 32, 451 '1.3.132.0.15': 21, 452 '1.3.132.0.16': 36, 453 '1.3.132.0.17': 36, 454 '1.3.132.0.22': 17, 455 '1.3.132.0.23': 17, 456 '1.3.132.0.24': 25, 457 '1.3.132.0.25': 25, 458 '1.3.132.0.26': 29, 459 '1.3.132.0.27': 30, 460 '1.3.132.0.28': 16, 461 '1.3.132.0.29': 16, 462 '1.3.132.0.30': 21, 463 '1.3.132.0.31': 24, 464 '1.3.132.0.32': 29, 465 '1.3.132.0.33': 28, 466 '1.3.132.0.34': 48, 467 '1.3.132.0.35': 66, 468 '1.3.132.0.36': 51, 469 '1.3.132.0.37': 52, 470 '1.3.132.0.38': 72, 471 '1.3.132.0.39': 72, 472 # Order values used to compute these sourced from 473 # https://tools.ietf.org/html/rfc5639#section-3 474 # ceil(q.bit_length() / 8) 475 '1.3.36.3.3.2.8.1.1.1': 20, 476 '1.3.36.3.3.2.8.1.1.2': 20, 477 '1.3.36.3.3.2.8.1.1.3': 24, 478 '1.3.36.3.3.2.8.1.1.4': 24, 479 '1.3.36.3.3.2.8.1.1.5': 28, 480 '1.3.36.3.3.2.8.1.1.6': 28, 481 '1.3.36.3.3.2.8.1.1.7': 32, 482 '1.3.36.3.3.2.8.1.1.8': 32, 483 '1.3.36.3.3.2.8.1.1.9': 40, 484 '1.3.36.3.3.2.8.1.1.10': 40, 485 '1.3.36.3.3.2.8.1.1.11': 48, 486 '1.3.36.3.3.2.8.1.1.12': 48, 487 '1.3.36.3.3.2.8.1.1.13': 64, 488 '1.3.36.3.3.2.8.1.1.14': 64, 489 } 490 491 @classmethod 492 def register(cls, name, oid, key_size): 493 """ 494 Registers a new named elliptic curve that is not included in the 495 default list of named curves 496 497 :param name: 498 A unicode string of the curve name 499 500 :param oid: 501 A unicode string of the dotted format OID 502 503 :param key_size: 504 An integer of the number of bytes the private key should be 505 encoded to 506 """ 507 508 cls._map[oid] = name 509 if cls._reverse_map is not None: 510 cls._reverse_map[name] = oid 511 cls._key_sizes[oid] = key_size 512 513 514class ECDomainParameters(Choice): 515 """ 516 Source: http://www.secg.org/sec1-v2.pdf page 102 517 """ 518 519 _alternatives = [ 520 ('specified', SpecifiedECDomain), 521 ('named', NamedCurve), 522 ('implicit_ca', Null), 523 ] 524 525 @property 526 def key_size(self): 527 if self.name == 'implicit_ca': 528 raise ValueError(unwrap( 529 ''' 530 Unable to calculate key_size from ECDomainParameters 531 that are implicitly defined by the CA key 532 ''' 533 )) 534 535 if self.name == 'specified': 536 order = self.chosen['order'].native 537 return math.ceil(math.log(order, 2.0) / 8.0) 538 539 oid = self.chosen.dotted 540 if oid not in NamedCurve._key_sizes: 541 raise ValueError(unwrap( 542 ''' 543 The asn1crypto.keys.NamedCurve %s does not have a registered key length, 544 please call asn1crypto.keys.NamedCurve.register() 545 ''', 546 repr(oid) 547 )) 548 return NamedCurve._key_sizes[oid] 549 550 551class ECPrivateKeyVersion(Integer): 552 """ 553 Original Name: None 554 Source: http://www.secg.org/sec1-v2.pdf page 108 555 """ 556 557 _map = { 558 1: 'ecPrivkeyVer1', 559 } 560 561 562class ECPrivateKey(Sequence): 563 """ 564 Source: http://www.secg.org/sec1-v2.pdf page 108 565 """ 566 567 _fields = [ 568 ('version', ECPrivateKeyVersion), 569 ('private_key', IntegerOctetString), 570 ('parameters', ECDomainParameters, {'explicit': 0, 'optional': True}), 571 ('public_key', ECPointBitString, {'explicit': 1, 'optional': True}), 572 ] 573 574 # Ensures the key is set to the correct length when encoding 575 _key_size = None 576 577 # This is necessary to ensure the private_key IntegerOctetString is encoded properly 578 def __setitem__(self, key, value): 579 res = super(ECPrivateKey, self).__setitem__(key, value) 580 581 if key == 'private_key': 582 if self._key_size is None: 583 # Infer the key_size from the existing private key if possible 584 pkey_contents = self['private_key'].contents 585 if isinstance(pkey_contents, byte_cls) and len(pkey_contents) > 1: 586 self.set_key_size(len(self['private_key'].contents)) 587 588 elif self._key_size is not None: 589 self._update_key_size() 590 591 elif key == 'parameters' and isinstance(self['parameters'], ECDomainParameters) and \ 592 self['parameters'].name != 'implicit_ca': 593 self.set_key_size(self['parameters'].key_size) 594 595 return res 596 597 def set_key_size(self, key_size): 598 """ 599 Sets the key_size to ensure the private key is encoded to the proper length 600 601 :param key_size: 602 An integer byte length to encode the private_key to 603 """ 604 605 self._key_size = key_size 606 self._update_key_size() 607 608 def _update_key_size(self): 609 """ 610 Ensure the private_key explicit encoding width is set 611 """ 612 613 if self._key_size is not None and isinstance(self['private_key'], IntegerOctetString): 614 self['private_key'].set_encoded_width(self._key_size) 615 616 617class DSAParams(Sequence): 618 """ 619 Parameters for a DSA public or private key 620 621 Original Name: Dss-Parms 622 Source: https://tools.ietf.org/html/rfc3279#page-9 623 """ 624 625 _fields = [ 626 ('p', Integer), 627 ('q', Integer), 628 ('g', Integer), 629 ] 630 631 632class Attribute(Sequence): 633 """ 634 Source: https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.501-198811-S!!PDF-E&type=items page 8 635 """ 636 637 _fields = [ 638 ('type', ObjectIdentifier), 639 ('values', SetOf, {'spec': Any}), 640 ] 641 642 643class Attributes(SetOf): 644 """ 645 Source: https://tools.ietf.org/html/rfc5208#page-3 646 """ 647 648 _child_spec = Attribute 649 650 651class PrivateKeyAlgorithmId(ObjectIdentifier): 652 """ 653 These OIDs for various public keys are reused when storing private keys 654 inside of a PKCS#8 structure 655 656 Original Name: None 657 Source: https://tools.ietf.org/html/rfc3279 658 """ 659 660 _map = { 661 # https://tools.ietf.org/html/rfc3279#page-19 662 '1.2.840.113549.1.1.1': 'rsa', 663 # https://tools.ietf.org/html/rfc4055#page-8 664 '1.2.840.113549.1.1.10': 'rsassa_pss', 665 # https://tools.ietf.org/html/rfc3279#page-18 666 '1.2.840.10040.4.1': 'dsa', 667 # https://tools.ietf.org/html/rfc3279#page-13 668 '1.2.840.10045.2.1': 'ec', 669 } 670 671 672class PrivateKeyAlgorithm(_ForceNullParameters, Sequence): 673 """ 674 Original Name: PrivateKeyAlgorithmIdentifier 675 Source: https://tools.ietf.org/html/rfc5208#page-3 676 """ 677 678 _fields = [ 679 ('algorithm', PrivateKeyAlgorithmId), 680 ('parameters', Any, {'optional': True}), 681 ] 682 683 _oid_pair = ('algorithm', 'parameters') 684 _oid_specs = { 685 'dsa': DSAParams, 686 'ec': ECDomainParameters, 687 'rsassa_pss': RSASSAPSSParams, 688 } 689 690 691class PrivateKeyInfo(Sequence): 692 """ 693 Source: https://tools.ietf.org/html/rfc5208#page-3 694 """ 695 696 _fields = [ 697 ('version', Integer), 698 ('private_key_algorithm', PrivateKeyAlgorithm), 699 ('private_key', ParsableOctetString), 700 ('attributes', Attributes, {'implicit': 0, 'optional': True}), 701 ] 702 703 def _private_key_spec(self): 704 algorithm = self['private_key_algorithm']['algorithm'].native 705 return { 706 'rsa': RSAPrivateKey, 707 'rsassa_pss': RSAPrivateKey, 708 'dsa': Integer, 709 'ec': ECPrivateKey, 710 }[algorithm] 711 712 _spec_callbacks = { 713 'private_key': _private_key_spec 714 } 715 716 _algorithm = None 717 _bit_size = None 718 _public_key = None 719 _fingerprint = None 720 721 @classmethod 722 def wrap(cls, private_key, algorithm): 723 """ 724 Wraps a private key in a PrivateKeyInfo structure 725 726 :param private_key: 727 A byte string or Asn1Value object of the private key 728 729 :param algorithm: 730 A unicode string of "rsa", "dsa" or "ec" 731 732 :return: 733 A PrivateKeyInfo object 734 """ 735 736 if not isinstance(private_key, byte_cls) and not isinstance(private_key, Asn1Value): 737 raise TypeError(unwrap( 738 ''' 739 private_key must be a byte string or Asn1Value, not %s 740 ''', 741 type_name(private_key) 742 )) 743 744 if algorithm == 'rsa': 745 if not isinstance(private_key, RSAPrivateKey): 746 private_key = RSAPrivateKey.load(private_key) 747 params = Null() 748 elif algorithm == 'dsa': 749 if not isinstance(private_key, DSAPrivateKey): 750 private_key = DSAPrivateKey.load(private_key) 751 params = DSAParams() 752 params['p'] = private_key['p'] 753 params['q'] = private_key['q'] 754 params['g'] = private_key['g'] 755 public_key = private_key['public_key'] 756 private_key = private_key['private_key'] 757 elif algorithm == 'ec': 758 if not isinstance(private_key, ECPrivateKey): 759 private_key = ECPrivateKey.load(private_key) 760 else: 761 private_key = private_key.copy() 762 params = private_key['parameters'] 763 del private_key['parameters'] 764 else: 765 raise ValueError(unwrap( 766 ''' 767 algorithm must be one of "rsa", "dsa", "ec", not %s 768 ''', 769 repr(algorithm) 770 )) 771 772 private_key_algo = PrivateKeyAlgorithm() 773 private_key_algo['algorithm'] = PrivateKeyAlgorithmId(algorithm) 774 private_key_algo['parameters'] = params 775 776 container = cls() 777 container._algorithm = algorithm 778 container['version'] = Integer(0) 779 container['private_key_algorithm'] = private_key_algo 780 container['private_key'] = private_key 781 782 # Here we save the DSA public key if possible since it is not contained 783 # within the PKCS#8 structure for a DSA key 784 if algorithm == 'dsa': 785 container._public_key = public_key 786 787 return container 788 789 # This is necessary to ensure any contained ECPrivateKey is the 790 # correct size 791 def __setitem__(self, key, value): 792 res = super(PrivateKeyInfo, self).__setitem__(key, value) 793 794 algorithm = self['private_key_algorithm'] 795 796 # When possible, use the parameter info to make sure the private key encoding 797 # retains any necessary leading bytes, instead of them being dropped 798 if (key == 'private_key_algorithm' or key == 'private_key') and \ 799 algorithm['algorithm'].native == 'ec' and \ 800 isinstance(algorithm['parameters'], ECDomainParameters) and \ 801 algorithm['parameters'].name != 'implicit_ca' and \ 802 isinstance(self['private_key'], ParsableOctetString) and \ 803 isinstance(self['private_key'].parsed, ECPrivateKey): 804 self['private_key'].parsed.set_key_size(algorithm['parameters'].key_size) 805 806 return res 807 808 def unwrap(self): 809 """ 810 Unwraps the private key into an RSAPrivateKey, DSAPrivateKey or 811 ECPrivateKey object 812 813 :return: 814 An RSAPrivateKey, DSAPrivateKey or ECPrivateKey object 815 """ 816 817 raise APIException( 818 'asn1crypto.keys.PrivateKeyInfo().unwrap() has been removed, ' 819 'please use oscrypto.asymmetric.PrivateKey().unwrap() instead') 820 821 @property 822 def curve(self): 823 """ 824 Returns information about the curve used for an EC key 825 826 :raises: 827 ValueError - when the key is not an EC key 828 829 :return: 830 A two-element tuple, with the first element being a unicode string 831 of "implicit_ca", "specified" or "named". If the first element is 832 "implicit_ca", the second is None. If "specified", the second is 833 an OrderedDict that is the native version of SpecifiedECDomain. If 834 "named", the second is a unicode string of the curve name. 835 """ 836 837 if self.algorithm != 'ec': 838 raise ValueError(unwrap( 839 ''' 840 Only EC keys have a curve, this key is %s 841 ''', 842 self.algorithm.upper() 843 )) 844 845 params = self['private_key_algorithm']['parameters'] 846 chosen = params.chosen 847 848 if params.name == 'implicit_ca': 849 value = None 850 else: 851 value = chosen.native 852 853 return (params.name, value) 854 855 @property 856 def hash_algo(self): 857 """ 858 Returns the name of the family of hash algorithms used to generate a 859 DSA key 860 861 :raises: 862 ValueError - when the key is not a DSA key 863 864 :return: 865 A unicode string of "sha1" or "sha2" 866 """ 867 868 if self.algorithm != 'dsa': 869 raise ValueError(unwrap( 870 ''' 871 Only DSA keys are generated using a hash algorithm, this key is 872 %s 873 ''', 874 self.algorithm.upper() 875 )) 876 877 byte_len = math.log(self['private_key_algorithm']['parameters']['q'].native, 2) / 8 878 879 return 'sha1' if byte_len <= 20 else 'sha2' 880 881 @property 882 def algorithm(self): 883 """ 884 :return: 885 A unicode string of "rsa", "dsa" or "ec" 886 """ 887 888 if self._algorithm is None: 889 self._algorithm = self['private_key_algorithm']['algorithm'].native 890 return self._algorithm 891 892 @property 893 def bit_size(self): 894 """ 895 :return: 896 The bit size of the private key, as an integer 897 """ 898 899 if self._bit_size is None: 900 if self.algorithm == 'rsa': 901 prime = self['private_key'].parsed['modulus'].native 902 elif self.algorithm == 'dsa': 903 prime = self['private_key_algorithm']['parameters']['p'].native 904 elif self.algorithm == 'ec': 905 prime = self['private_key'].parsed['private_key'].native 906 self._bit_size = int(math.ceil(math.log(prime, 2))) 907 modulus = self._bit_size % 8 908 if modulus != 0: 909 self._bit_size += 8 - modulus 910 return self._bit_size 911 912 @property 913 def byte_size(self): 914 """ 915 :return: 916 The byte size of the private key, as an integer 917 """ 918 919 return int(math.ceil(self.bit_size / 8)) 920 921 @property 922 def public_key(self): 923 """ 924 :return: 925 If an RSA key, an RSAPublicKey object. If a DSA key, an Integer 926 object. If an EC key, an ECPointBitString object. 927 """ 928 929 raise APIException( 930 'asn1crypto.keys.PrivateKeyInfo().public_key has been removed, ' 931 'please use oscrypto.asymmetric.PrivateKey().public_key.unwrap() instead') 932 933 @property 934 def public_key_info(self): 935 """ 936 :return: 937 A PublicKeyInfo object derived from this private key. 938 """ 939 940 raise APIException( 941 'asn1crypto.keys.PrivateKeyInfo().public_key_info has been removed, ' 942 'please use oscrypto.asymmetric.PrivateKey().public_key.asn1 instead') 943 944 @property 945 def fingerprint(self): 946 """ 947 Creates a fingerprint that can be compared with a public key to see if 948 the two form a pair. 949 950 This fingerprint is not compatible with fingerprints generated by any 951 other software. 952 953 :return: 954 A byte string that is a sha256 hash of selected components (based 955 on the key type) 956 """ 957 958 raise APIException( 959 'asn1crypto.keys.PrivateKeyInfo().fingerprint has been removed, ' 960 'please use oscrypto.asymmetric.PrivateKey().fingerprint instead') 961 962 963class EncryptedPrivateKeyInfo(Sequence): 964 """ 965 Source: https://tools.ietf.org/html/rfc5208#page-4 966 """ 967 968 _fields = [ 969 ('encryption_algorithm', EncryptionAlgorithm), 970 ('encrypted_data', OctetString), 971 ] 972 973 974# These structures are from https://tools.ietf.org/html/rfc3279 975 976class ValidationParms(Sequence): 977 """ 978 Source: https://tools.ietf.org/html/rfc3279#page-10 979 """ 980 981 _fields = [ 982 ('seed', BitString), 983 ('pgen_counter', Integer), 984 ] 985 986 987class DomainParameters(Sequence): 988 """ 989 Source: https://tools.ietf.org/html/rfc3279#page-10 990 """ 991 992 _fields = [ 993 ('p', Integer), 994 ('g', Integer), 995 ('q', Integer), 996 ('j', Integer, {'optional': True}), 997 ('validation_params', ValidationParms, {'optional': True}), 998 ] 999 1000 1001class PublicKeyAlgorithmId(ObjectIdentifier): 1002 """ 1003 Original Name: None 1004 Source: https://tools.ietf.org/html/rfc3279 1005 """ 1006 1007 _map = { 1008 # https://tools.ietf.org/html/rfc3279#page-19 1009 '1.2.840.113549.1.1.1': 'rsa', 1010 # https://tools.ietf.org/html/rfc3447#page-47 1011 '1.2.840.113549.1.1.7': 'rsaes_oaep', 1012 # https://tools.ietf.org/html/rfc4055#page-8 1013 '1.2.840.113549.1.1.10': 'rsassa_pss', 1014 # https://tools.ietf.org/html/rfc3279#page-18 1015 '1.2.840.10040.4.1': 'dsa', 1016 # https://tools.ietf.org/html/rfc3279#page-13 1017 '1.2.840.10045.2.1': 'ec', 1018 # https://tools.ietf.org/html/rfc3279#page-10 1019 '1.2.840.10046.2.1': 'dh', 1020 } 1021 1022 1023class PublicKeyAlgorithm(_ForceNullParameters, Sequence): 1024 """ 1025 Original Name: AlgorithmIdentifier 1026 Source: https://tools.ietf.org/html/rfc5280#page-18 1027 """ 1028 1029 _fields = [ 1030 ('algorithm', PublicKeyAlgorithmId), 1031 ('parameters', Any, {'optional': True}), 1032 ] 1033 1034 _oid_pair = ('algorithm', 'parameters') 1035 _oid_specs = { 1036 'dsa': DSAParams, 1037 'ec': ECDomainParameters, 1038 'dh': DomainParameters, 1039 'rsaes_oaep': RSAESOAEPParams, 1040 'rsassa_pss': RSASSAPSSParams, 1041 } 1042 1043 1044class PublicKeyInfo(Sequence): 1045 """ 1046 Original Name: SubjectPublicKeyInfo 1047 Source: https://tools.ietf.org/html/rfc5280#page-17 1048 """ 1049 1050 _fields = [ 1051 ('algorithm', PublicKeyAlgorithm), 1052 ('public_key', ParsableOctetBitString), 1053 ] 1054 1055 def _public_key_spec(self): 1056 algorithm = self['algorithm']['algorithm'].native 1057 return { 1058 'rsa': RSAPublicKey, 1059 'rsaes_oaep': RSAPublicKey, 1060 'rsassa_pss': RSAPublicKey, 1061 'dsa': Integer, 1062 # We override the field spec with ECPoint so that users can easily 1063 # decompose the byte string into the constituent X and Y coords 1064 'ec': (ECPointBitString, None), 1065 'dh': Integer, 1066 }[algorithm] 1067 1068 _spec_callbacks = { 1069 'public_key': _public_key_spec 1070 } 1071 1072 _algorithm = None 1073 _bit_size = None 1074 _fingerprint = None 1075 _sha1 = None 1076 _sha256 = None 1077 1078 @classmethod 1079 def wrap(cls, public_key, algorithm): 1080 """ 1081 Wraps a public key in a PublicKeyInfo structure 1082 1083 :param public_key: 1084 A byte string or Asn1Value object of the public key 1085 1086 :param algorithm: 1087 A unicode string of "rsa" 1088 1089 :return: 1090 A PublicKeyInfo object 1091 """ 1092 1093 if not isinstance(public_key, byte_cls) and not isinstance(public_key, Asn1Value): 1094 raise TypeError(unwrap( 1095 ''' 1096 public_key must be a byte string or Asn1Value, not %s 1097 ''', 1098 type_name(public_key) 1099 )) 1100 1101 if algorithm != 'rsa': 1102 raise ValueError(unwrap( 1103 ''' 1104 algorithm must "rsa", not %s 1105 ''', 1106 repr(algorithm) 1107 )) 1108 1109 algo = PublicKeyAlgorithm() 1110 algo['algorithm'] = PublicKeyAlgorithmId(algorithm) 1111 algo['parameters'] = Null() 1112 1113 container = cls() 1114 container['algorithm'] = algo 1115 if isinstance(public_key, Asn1Value): 1116 public_key = public_key.untag().dump() 1117 container['public_key'] = ParsableOctetBitString(public_key) 1118 1119 return container 1120 1121 def unwrap(self): 1122 """ 1123 Unwraps an RSA public key into an RSAPublicKey object. Does not support 1124 DSA or EC public keys since they do not have an unwrapped form. 1125 1126 :return: 1127 An RSAPublicKey object 1128 """ 1129 1130 raise APIException( 1131 'asn1crypto.keys.PublicKeyInfo().unwrap() has been removed, ' 1132 'please use oscrypto.asymmetric.PublicKey().unwrap() instead') 1133 1134 @property 1135 def curve(self): 1136 """ 1137 Returns information about the curve used for an EC key 1138 1139 :raises: 1140 ValueError - when the key is not an EC key 1141 1142 :return: 1143 A two-element tuple, with the first element being a unicode string 1144 of "implicit_ca", "specified" or "named". If the first element is 1145 "implicit_ca", the second is None. If "specified", the second is 1146 an OrderedDict that is the native version of SpecifiedECDomain. If 1147 "named", the second is a unicode string of the curve name. 1148 """ 1149 1150 if self.algorithm != 'ec': 1151 raise ValueError(unwrap( 1152 ''' 1153 Only EC keys have a curve, this key is %s 1154 ''', 1155 self.algorithm.upper() 1156 )) 1157 1158 params = self['algorithm']['parameters'] 1159 chosen = params.chosen 1160 1161 if params.name == 'implicit_ca': 1162 value = None 1163 else: 1164 value = chosen.native 1165 1166 return (params.name, value) 1167 1168 @property 1169 def hash_algo(self): 1170 """ 1171 Returns the name of the family of hash algorithms used to generate a 1172 DSA key 1173 1174 :raises: 1175 ValueError - when the key is not a DSA key 1176 1177 :return: 1178 A unicode string of "sha1" or "sha2" or None if no parameters are 1179 present 1180 """ 1181 1182 if self.algorithm != 'dsa': 1183 raise ValueError(unwrap( 1184 ''' 1185 Only DSA keys are generated using a hash algorithm, this key is 1186 %s 1187 ''', 1188 self.algorithm.upper() 1189 )) 1190 1191 parameters = self['algorithm']['parameters'] 1192 if parameters.native is None: 1193 return None 1194 1195 byte_len = math.log(parameters['q'].native, 2) / 8 1196 1197 return 'sha1' if byte_len <= 20 else 'sha2' 1198 1199 @property 1200 def algorithm(self): 1201 """ 1202 :return: 1203 A unicode string of "rsa", "dsa" or "ec" 1204 """ 1205 1206 if self._algorithm is None: 1207 self._algorithm = self['algorithm']['algorithm'].native 1208 return self._algorithm 1209 1210 @property 1211 def bit_size(self): 1212 """ 1213 :return: 1214 The bit size of the public key, as an integer 1215 """ 1216 1217 if self._bit_size is None: 1218 if self.algorithm == 'ec': 1219 self._bit_size = int(((len(self['public_key'].native) - 1) / 2) * 8) 1220 else: 1221 if self.algorithm == 'rsa': 1222 prime = self['public_key'].parsed['modulus'].native 1223 elif self.algorithm == 'dsa': 1224 prime = self['algorithm']['parameters']['p'].native 1225 self._bit_size = int(math.ceil(math.log(prime, 2))) 1226 modulus = self._bit_size % 8 1227 if modulus != 0: 1228 self._bit_size += 8 - modulus 1229 1230 return self._bit_size 1231 1232 @property 1233 def byte_size(self): 1234 """ 1235 :return: 1236 The byte size of the public key, as an integer 1237 """ 1238 1239 return int(math.ceil(self.bit_size / 8)) 1240 1241 @property 1242 def sha1(self): 1243 """ 1244 :return: 1245 The SHA1 hash of the DER-encoded bytes of this public key info 1246 """ 1247 1248 if self._sha1 is None: 1249 self._sha1 = hashlib.sha1(byte_cls(self['public_key'])).digest() 1250 return self._sha1 1251 1252 @property 1253 def sha256(self): 1254 """ 1255 :return: 1256 The SHA-256 hash of the DER-encoded bytes of this public key info 1257 """ 1258 1259 if self._sha256 is None: 1260 self._sha256 = hashlib.sha256(byte_cls(self['public_key'])).digest() 1261 return self._sha256 1262 1263 @property 1264 def fingerprint(self): 1265 """ 1266 Creates a fingerprint that can be compared with a private key to see if 1267 the two form a pair. 1268 1269 This fingerprint is not compatible with fingerprints generated by any 1270 other software. 1271 1272 :return: 1273 A byte string that is a sha256 hash of selected components (based 1274 on the key type) 1275 """ 1276 1277 raise APIException( 1278 'asn1crypto.keys.PublicKeyInfo().fingerprint has been removed, ' 1279 'please use oscrypto.asymmetric.PublicKey().fingerprint instead') 1280