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 # https://tools.ietf.org/html/rfc8410#section-9 670 '1.3.101.110': 'x25519', 671 '1.3.101.111': 'x448', 672 '1.3.101.112': 'ed25519', 673 '1.3.101.113': 'ed448', 674 } 675 676 677class PrivateKeyAlgorithm(_ForceNullParameters, Sequence): 678 """ 679 Original Name: PrivateKeyAlgorithmIdentifier 680 Source: https://tools.ietf.org/html/rfc5208#page-3 681 """ 682 683 _fields = [ 684 ('algorithm', PrivateKeyAlgorithmId), 685 ('parameters', Any, {'optional': True}), 686 ] 687 688 _oid_pair = ('algorithm', 'parameters') 689 _oid_specs = { 690 'dsa': DSAParams, 691 'ec': ECDomainParameters, 692 'rsassa_pss': RSASSAPSSParams, 693 } 694 695 696class PrivateKeyInfo(Sequence): 697 """ 698 Source: https://tools.ietf.org/html/rfc5208#page-3 699 """ 700 701 _fields = [ 702 ('version', Integer), 703 ('private_key_algorithm', PrivateKeyAlgorithm), 704 ('private_key', ParsableOctetString), 705 ('attributes', Attributes, {'implicit': 0, 'optional': True}), 706 ] 707 708 def _private_key_spec(self): 709 algorithm = self['private_key_algorithm']['algorithm'].native 710 return { 711 'rsa': RSAPrivateKey, 712 'rsassa_pss': RSAPrivateKey, 713 'dsa': Integer, 714 'ec': ECPrivateKey, 715 # These should be treated as opaque octet strings according 716 # to RFC 8410 717 'x25519': OctetString, 718 'x448': OctetString, 719 'ed25519': OctetString, 720 'ed448': OctetString, 721 }[algorithm] 722 723 _spec_callbacks = { 724 'private_key': _private_key_spec 725 } 726 727 _algorithm = None 728 _bit_size = None 729 _public_key = None 730 _fingerprint = None 731 732 @classmethod 733 def wrap(cls, private_key, algorithm): 734 """ 735 Wraps a private key in a PrivateKeyInfo structure 736 737 :param private_key: 738 A byte string or Asn1Value object of the private key 739 740 :param algorithm: 741 A unicode string of "rsa", "dsa" or "ec" 742 743 :return: 744 A PrivateKeyInfo object 745 """ 746 747 if not isinstance(private_key, byte_cls) and not isinstance(private_key, Asn1Value): 748 raise TypeError(unwrap( 749 ''' 750 private_key must be a byte string or Asn1Value, not %s 751 ''', 752 type_name(private_key) 753 )) 754 755 if algorithm == 'rsa' or algorithm == 'rsassa_pss': 756 if not isinstance(private_key, RSAPrivateKey): 757 private_key = RSAPrivateKey.load(private_key) 758 params = Null() 759 elif algorithm == 'dsa': 760 if not isinstance(private_key, DSAPrivateKey): 761 private_key = DSAPrivateKey.load(private_key) 762 params = DSAParams() 763 params['p'] = private_key['p'] 764 params['q'] = private_key['q'] 765 params['g'] = private_key['g'] 766 public_key = private_key['public_key'] 767 private_key = private_key['private_key'] 768 elif algorithm == 'ec': 769 if not isinstance(private_key, ECPrivateKey): 770 private_key = ECPrivateKey.load(private_key) 771 else: 772 private_key = private_key.copy() 773 params = private_key['parameters'] 774 del private_key['parameters'] 775 else: 776 raise ValueError(unwrap( 777 ''' 778 algorithm must be one of "rsa", "dsa", "ec", not %s 779 ''', 780 repr(algorithm) 781 )) 782 783 private_key_algo = PrivateKeyAlgorithm() 784 private_key_algo['algorithm'] = PrivateKeyAlgorithmId(algorithm) 785 private_key_algo['parameters'] = params 786 787 container = cls() 788 container._algorithm = algorithm 789 container['version'] = Integer(0) 790 container['private_key_algorithm'] = private_key_algo 791 container['private_key'] = private_key 792 793 # Here we save the DSA public key if possible since it is not contained 794 # within the PKCS#8 structure for a DSA key 795 if algorithm == 'dsa': 796 container._public_key = public_key 797 798 return container 799 800 # This is necessary to ensure any contained ECPrivateKey is the 801 # correct size 802 def __setitem__(self, key, value): 803 res = super(PrivateKeyInfo, self).__setitem__(key, value) 804 805 algorithm = self['private_key_algorithm'] 806 807 # When possible, use the parameter info to make sure the private key encoding 808 # retains any necessary leading bytes, instead of them being dropped 809 if (key == 'private_key_algorithm' or key == 'private_key') and \ 810 algorithm['algorithm'].native == 'ec' and \ 811 isinstance(algorithm['parameters'], ECDomainParameters) and \ 812 algorithm['parameters'].name != 'implicit_ca' and \ 813 isinstance(self['private_key'], ParsableOctetString) and \ 814 isinstance(self['private_key'].parsed, ECPrivateKey): 815 self['private_key'].parsed.set_key_size(algorithm['parameters'].key_size) 816 817 return res 818 819 def unwrap(self): 820 """ 821 Unwraps the private key into an RSAPrivateKey, DSAPrivateKey or 822 ECPrivateKey object 823 824 :return: 825 An RSAPrivateKey, DSAPrivateKey or ECPrivateKey object 826 """ 827 828 raise APIException( 829 'asn1crypto.keys.PrivateKeyInfo().unwrap() has been removed, ' 830 'please use oscrypto.asymmetric.PrivateKey().unwrap() instead') 831 832 @property 833 def curve(self): 834 """ 835 Returns information about the curve used for an EC key 836 837 :raises: 838 ValueError - when the key is not an EC key 839 840 :return: 841 A two-element tuple, with the first element being a unicode string 842 of "implicit_ca", "specified" or "named". If the first element is 843 "implicit_ca", the second is None. If "specified", the second is 844 an OrderedDict that is the native version of SpecifiedECDomain. If 845 "named", the second is a unicode string of the curve name. 846 """ 847 848 if self.algorithm != 'ec': 849 raise ValueError(unwrap( 850 ''' 851 Only EC keys have a curve, this key is %s 852 ''', 853 self.algorithm.upper() 854 )) 855 856 params = self['private_key_algorithm']['parameters'] 857 chosen = params.chosen 858 859 if params.name == 'implicit_ca': 860 value = None 861 else: 862 value = chosen.native 863 864 return (params.name, value) 865 866 @property 867 def hash_algo(self): 868 """ 869 Returns the name of the family of hash algorithms used to generate a 870 DSA key 871 872 :raises: 873 ValueError - when the key is not a DSA key 874 875 :return: 876 A unicode string of "sha1" or "sha2" 877 """ 878 879 if self.algorithm != 'dsa': 880 raise ValueError(unwrap( 881 ''' 882 Only DSA keys are generated using a hash algorithm, this key is 883 %s 884 ''', 885 self.algorithm.upper() 886 )) 887 888 byte_len = math.log(self['private_key_algorithm']['parameters']['q'].native, 2) / 8 889 890 return 'sha1' if byte_len <= 20 else 'sha2' 891 892 @property 893 def algorithm(self): 894 """ 895 :return: 896 A unicode string of "rsa", "rsassa_pss", "dsa" or "ec" 897 """ 898 899 if self._algorithm is None: 900 self._algorithm = self['private_key_algorithm']['algorithm'].native 901 return self._algorithm 902 903 @property 904 def bit_size(self): 905 """ 906 :return: 907 The bit size of the private key, as an integer 908 """ 909 910 if self._bit_size is None: 911 if self.algorithm == 'rsa' or self.algorithm == 'rsassa_pss': 912 prime = self['private_key'].parsed['modulus'].native 913 elif self.algorithm == 'dsa': 914 prime = self['private_key_algorithm']['parameters']['p'].native 915 elif self.algorithm == 'ec': 916 prime = self['private_key'].parsed['private_key'].native 917 self._bit_size = int(math.ceil(math.log(prime, 2))) 918 modulus = self._bit_size % 8 919 if modulus != 0: 920 self._bit_size += 8 - modulus 921 return self._bit_size 922 923 @property 924 def byte_size(self): 925 """ 926 :return: 927 The byte size of the private key, as an integer 928 """ 929 930 return int(math.ceil(self.bit_size / 8)) 931 932 @property 933 def public_key(self): 934 """ 935 :return: 936 If an RSA key, an RSAPublicKey object. If a DSA key, an Integer 937 object. If an EC key, an ECPointBitString object. 938 """ 939 940 raise APIException( 941 'asn1crypto.keys.PrivateKeyInfo().public_key has been removed, ' 942 'please use oscrypto.asymmetric.PrivateKey().public_key.unwrap() instead') 943 944 @property 945 def public_key_info(self): 946 """ 947 :return: 948 A PublicKeyInfo object derived from this private key. 949 """ 950 951 raise APIException( 952 'asn1crypto.keys.PrivateKeyInfo().public_key_info has been removed, ' 953 'please use oscrypto.asymmetric.PrivateKey().public_key.asn1 instead') 954 955 @property 956 def fingerprint(self): 957 """ 958 Creates a fingerprint that can be compared with a public key to see if 959 the two form a pair. 960 961 This fingerprint is not compatible with fingerprints generated by any 962 other software. 963 964 :return: 965 A byte string that is a sha256 hash of selected components (based 966 on the key type) 967 """ 968 969 raise APIException( 970 'asn1crypto.keys.PrivateKeyInfo().fingerprint has been removed, ' 971 'please use oscrypto.asymmetric.PrivateKey().fingerprint instead') 972 973 974class EncryptedPrivateKeyInfo(Sequence): 975 """ 976 Source: https://tools.ietf.org/html/rfc5208#page-4 977 """ 978 979 _fields = [ 980 ('encryption_algorithm', EncryptionAlgorithm), 981 ('encrypted_data', OctetString), 982 ] 983 984 985# These structures are from https://tools.ietf.org/html/rfc3279 986 987class ValidationParms(Sequence): 988 """ 989 Source: https://tools.ietf.org/html/rfc3279#page-10 990 """ 991 992 _fields = [ 993 ('seed', BitString), 994 ('pgen_counter', Integer), 995 ] 996 997 998class DomainParameters(Sequence): 999 """ 1000 Source: https://tools.ietf.org/html/rfc3279#page-10 1001 """ 1002 1003 _fields = [ 1004 ('p', Integer), 1005 ('g', Integer), 1006 ('q', Integer), 1007 ('j', Integer, {'optional': True}), 1008 ('validation_params', ValidationParms, {'optional': True}), 1009 ] 1010 1011 1012class PublicKeyAlgorithmId(ObjectIdentifier): 1013 """ 1014 Original Name: None 1015 Source: https://tools.ietf.org/html/rfc3279 1016 """ 1017 1018 _map = { 1019 # https://tools.ietf.org/html/rfc3279#page-19 1020 '1.2.840.113549.1.1.1': 'rsa', 1021 # https://tools.ietf.org/html/rfc3447#page-47 1022 '1.2.840.113549.1.1.7': 'rsaes_oaep', 1023 # https://tools.ietf.org/html/rfc4055#page-8 1024 '1.2.840.113549.1.1.10': 'rsassa_pss', 1025 # https://tools.ietf.org/html/rfc3279#page-18 1026 '1.2.840.10040.4.1': 'dsa', 1027 # https://tools.ietf.org/html/rfc3279#page-13 1028 '1.2.840.10045.2.1': 'ec', 1029 # https://tools.ietf.org/html/rfc3279#page-10 1030 '1.2.840.10046.2.1': 'dh', 1031 # https://tools.ietf.org/html/rfc8410#section-9 1032 '1.3.101.110': 'x25519', 1033 '1.3.101.111': 'x448', 1034 '1.3.101.112': 'ed25519', 1035 '1.3.101.113': 'ed448', 1036 } 1037 1038 1039class PublicKeyAlgorithm(_ForceNullParameters, Sequence): 1040 """ 1041 Original Name: AlgorithmIdentifier 1042 Source: https://tools.ietf.org/html/rfc5280#page-18 1043 """ 1044 1045 _fields = [ 1046 ('algorithm', PublicKeyAlgorithmId), 1047 ('parameters', Any, {'optional': True}), 1048 ] 1049 1050 _oid_pair = ('algorithm', 'parameters') 1051 _oid_specs = { 1052 'dsa': DSAParams, 1053 'ec': ECDomainParameters, 1054 'dh': DomainParameters, 1055 'rsaes_oaep': RSAESOAEPParams, 1056 'rsassa_pss': RSASSAPSSParams, 1057 } 1058 1059 1060class PublicKeyInfo(Sequence): 1061 """ 1062 Original Name: SubjectPublicKeyInfo 1063 Source: https://tools.ietf.org/html/rfc5280#page-17 1064 """ 1065 1066 _fields = [ 1067 ('algorithm', PublicKeyAlgorithm), 1068 ('public_key', ParsableOctetBitString), 1069 ] 1070 1071 def _public_key_spec(self): 1072 algorithm = self['algorithm']['algorithm'].native 1073 return { 1074 'rsa': RSAPublicKey, 1075 'rsaes_oaep': RSAPublicKey, 1076 'rsassa_pss': RSAPublicKey, 1077 'dsa': Integer, 1078 # We override the field spec with ECPoint so that users can easily 1079 # decompose the byte string into the constituent X and Y coords 1080 'ec': (ECPointBitString, None), 1081 'dh': Integer, 1082 # These should be treated as opaque bit strings according 1083 # to RFC 8410, and need not even be valid ASN.1 1084 'x25519': (OctetBitString, None), 1085 'x448': (OctetBitString, None), 1086 'ed25519': (OctetBitString, None), 1087 'ed448': (OctetBitString, None), 1088 }[algorithm] 1089 1090 _spec_callbacks = { 1091 'public_key': _public_key_spec 1092 } 1093 1094 _algorithm = None 1095 _bit_size = None 1096 _fingerprint = None 1097 _sha1 = None 1098 _sha256 = None 1099 1100 @classmethod 1101 def wrap(cls, public_key, algorithm): 1102 """ 1103 Wraps a public key in a PublicKeyInfo structure 1104 1105 :param public_key: 1106 A byte string or Asn1Value object of the public key 1107 1108 :param algorithm: 1109 A unicode string of "rsa" 1110 1111 :return: 1112 A PublicKeyInfo object 1113 """ 1114 1115 if not isinstance(public_key, byte_cls) and not isinstance(public_key, Asn1Value): 1116 raise TypeError(unwrap( 1117 ''' 1118 public_key must be a byte string or Asn1Value, not %s 1119 ''', 1120 type_name(public_key) 1121 )) 1122 1123 if algorithm != 'rsa' and algorithm != 'rsassa_pss': 1124 raise ValueError(unwrap( 1125 ''' 1126 algorithm must "rsa", not %s 1127 ''', 1128 repr(algorithm) 1129 )) 1130 1131 algo = PublicKeyAlgorithm() 1132 algo['algorithm'] = PublicKeyAlgorithmId(algorithm) 1133 algo['parameters'] = Null() 1134 1135 container = cls() 1136 container['algorithm'] = algo 1137 if isinstance(public_key, Asn1Value): 1138 public_key = public_key.untag().dump() 1139 container['public_key'] = ParsableOctetBitString(public_key) 1140 1141 return container 1142 1143 def unwrap(self): 1144 """ 1145 Unwraps an RSA public key into an RSAPublicKey object. Does not support 1146 DSA or EC public keys since they do not have an unwrapped form. 1147 1148 :return: 1149 An RSAPublicKey object 1150 """ 1151 1152 raise APIException( 1153 'asn1crypto.keys.PublicKeyInfo().unwrap() has been removed, ' 1154 'please use oscrypto.asymmetric.PublicKey().unwrap() instead') 1155 1156 @property 1157 def curve(self): 1158 """ 1159 Returns information about the curve used for an EC key 1160 1161 :raises: 1162 ValueError - when the key is not an EC key 1163 1164 :return: 1165 A two-element tuple, with the first element being a unicode string 1166 of "implicit_ca", "specified" or "named". If the first element is 1167 "implicit_ca", the second is None. If "specified", the second is 1168 an OrderedDict that is the native version of SpecifiedECDomain. If 1169 "named", the second is a unicode string of the curve name. 1170 """ 1171 1172 if self.algorithm != 'ec': 1173 raise ValueError(unwrap( 1174 ''' 1175 Only EC keys have a curve, this key is %s 1176 ''', 1177 self.algorithm.upper() 1178 )) 1179 1180 params = self['algorithm']['parameters'] 1181 chosen = params.chosen 1182 1183 if params.name == 'implicit_ca': 1184 value = None 1185 else: 1186 value = chosen.native 1187 1188 return (params.name, value) 1189 1190 @property 1191 def hash_algo(self): 1192 """ 1193 Returns the name of the family of hash algorithms used to generate a 1194 DSA key 1195 1196 :raises: 1197 ValueError - when the key is not a DSA key 1198 1199 :return: 1200 A unicode string of "sha1" or "sha2" or None if no parameters are 1201 present 1202 """ 1203 1204 if self.algorithm != 'dsa': 1205 raise ValueError(unwrap( 1206 ''' 1207 Only DSA keys are generated using a hash algorithm, this key is 1208 %s 1209 ''', 1210 self.algorithm.upper() 1211 )) 1212 1213 parameters = self['algorithm']['parameters'] 1214 if parameters.native is None: 1215 return None 1216 1217 byte_len = math.log(parameters['q'].native, 2) / 8 1218 1219 return 'sha1' if byte_len <= 20 else 'sha2' 1220 1221 @property 1222 def algorithm(self): 1223 """ 1224 :return: 1225 A unicode string of "rsa", "rsassa_pss", "dsa" or "ec" 1226 """ 1227 1228 if self._algorithm is None: 1229 self._algorithm = self['algorithm']['algorithm'].native 1230 return self._algorithm 1231 1232 @property 1233 def bit_size(self): 1234 """ 1235 :return: 1236 The bit size of the public key, as an integer 1237 """ 1238 1239 if self._bit_size is None: 1240 if self.algorithm == 'ec': 1241 self._bit_size = int(((len(self['public_key'].native) - 1) / 2) * 8) 1242 else: 1243 if self.algorithm == 'rsa' or self.algorithm == 'rsassa_pss': 1244 prime = self['public_key'].parsed['modulus'].native 1245 elif self.algorithm == 'dsa': 1246 prime = self['algorithm']['parameters']['p'].native 1247 self._bit_size = int(math.ceil(math.log(prime, 2))) 1248 modulus = self._bit_size % 8 1249 if modulus != 0: 1250 self._bit_size += 8 - modulus 1251 1252 return self._bit_size 1253 1254 @property 1255 def byte_size(self): 1256 """ 1257 :return: 1258 The byte size of the public key, as an integer 1259 """ 1260 1261 return int(math.ceil(self.bit_size / 8)) 1262 1263 @property 1264 def sha1(self): 1265 """ 1266 :return: 1267 The SHA1 hash of the DER-encoded bytes of this public key info 1268 """ 1269 1270 if self._sha1 is None: 1271 self._sha1 = hashlib.sha1(byte_cls(self['public_key'])).digest() 1272 return self._sha1 1273 1274 @property 1275 def sha256(self): 1276 """ 1277 :return: 1278 The SHA-256 hash of the DER-encoded bytes of this public key info 1279 """ 1280 1281 if self._sha256 is None: 1282 self._sha256 = hashlib.sha256(byte_cls(self['public_key'])).digest() 1283 return self._sha256 1284 1285 @property 1286 def fingerprint(self): 1287 """ 1288 Creates a fingerprint that can be compared with a private key to see if 1289 the two form a pair. 1290 1291 This fingerprint is not compatible with fingerprints generated by any 1292 other software. 1293 1294 :return: 1295 A byte string that is a sha256 hash of selected components (based 1296 on the key type) 1297 """ 1298 1299 raise APIException( 1300 'asn1crypto.keys.PublicKeyInfo().fingerprint has been removed, ' 1301 'please use oscrypto.asymmetric.PublicKey().fingerprint instead') 1302