Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/asn1crypto/keys.py: 70%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# coding: utf-8
3"""
4ASN.1 type classes for public and private keys. Exports the following items:
6 - DSAPrivateKey()
7 - ECPrivateKey()
8 - EncryptedPrivateKeyInfo()
9 - PrivateKeyInfo()
10 - PublicKeyInfo()
11 - RSAPrivateKey()
12 - RSAPublicKey()
14Other type classes are defined that help compose the types listed above.
15"""
17from __future__ import unicode_literals, division, absolute_import, print_function
19import hashlib
20import math
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
45class OtherPrimeInfo(Sequence):
46 """
47 Source: https://tools.ietf.org/html/rfc3447#page-46
48 """
50 _fields = [
51 ('prime', Integer),
52 ('exponent', Integer),
53 ('coefficient', Integer),
54 ]
57class OtherPrimeInfos(SequenceOf):
58 """
59 Source: https://tools.ietf.org/html/rfc3447#page-46
60 """
62 _child_spec = OtherPrimeInfo
65class RSAPrivateKeyVersion(Integer):
66 """
67 Original Name: Version
68 Source: https://tools.ietf.org/html/rfc3447#page-45
69 """
71 _map = {
72 0: 'two-prime',
73 1: 'multi',
74 }
77class RSAPrivateKey(Sequence):
78 """
79 Source: https://tools.ietf.org/html/rfc3447#page-45
80 """
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 ]
96class RSAPublicKey(Sequence):
97 """
98 Source: https://tools.ietf.org/html/rfc3447#page-44
99 """
101 _fields = [
102 ('modulus', Integer),
103 ('public_exponent', Integer)
104 ]
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.
113 Original Name: None
114 Source: https://www.openssl.org/docs/apps/dsa.html
115 """
117 _fields = [
118 ('version', Integer),
119 ('p', Integer),
120 ('q', Integer),
121 ('g', Integer),
122 ('public_key', Integer),
123 ('private_key', Integer),
124 ]
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 """
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
141 :param x:
142 The X coordinate, as an integer
144 :param y:
145 The Y coordinate, as an integer
147 :return:
148 An ECPoint object
149 """
151 x_bytes = int(math.ceil(math.log(x, 2) / 8.0))
152 y_bytes = int(math.ceil(math.log(y, 2) / 8.0))
154 num_bytes = max(x_bytes, y_bytes)
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)
160 return cls(byte_string)
162 def to_coords(self):
163 """
164 Returns the X and Y coordinates for this EC point, as native Python
165 integers
167 :return:
168 A 2-element tuple containing integers (X, Y)
169 """
171 data = self.native
172 first_byte = data[0:1]
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)
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 ))
189 raise ValueError(unwrap(
190 '''
191 Compressed representations of EC public keys are not supported due
192 to patent US6252960
193 '''
194 ))
197class ECPoint(OctetString, _ECPoint):
199 pass
202class ECPointBitString(OctetBitString, _ECPoint):
204 pass
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 }
218class FieldType(ObjectIdentifier):
219 """
220 Original Name: None
221 Source: http://www.secg.org/sec1-v2.pdf page 101
222 """
224 _map = {
225 '1.2.840.10045.1.1': 'prime_field',
226 '1.2.840.10045.1.2': 'characteristic_two_field',
227 }
230class CharacteristicTwoBasis(ObjectIdentifier):
231 """
232 Original Name: None
233 Source: http://www.secg.org/sec1-v2.pdf page 102
234 """
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 }
243class Pentanomial(Sequence):
244 """
245 Source: http://www.secg.org/sec1-v2.pdf page 102
246 """
248 _fields = [
249 ('k1', Integer),
250 ('k2', Integer),
251 ('k3', Integer),
252 ]
255class CharacteristicTwo(Sequence):
256 """
257 Original Name: Characteristic-two
258 Source: http://www.secg.org/sec1-v2.pdf page 101
259 """
261 _fields = [
262 ('m', Integer),
263 ('basis', CharacteristicTwoBasis),
264 ('parameters', Any),
265 ]
267 _oid_pair = ('basis', 'parameters')
268 _oid_specs = {
269 'gn_basis': Null,
270 'tp_basis': Integer,
271 'pp_basis': Pentanomial,
272 }
275class FieldID(Sequence):
276 """
277 Source: http://www.secg.org/sec1-v2.pdf page 100
278 """
280 _fields = [
281 ('field_type', FieldType),
282 ('parameters', Any),
283 ]
285 _oid_pair = ('field_type', 'parameters')
286 _oid_specs = {
287 'prime_field': Integer,
288 'characteristic_two_field': CharacteristicTwo,
289 }
292class Curve(Sequence):
293 """
294 Source: http://www.secg.org/sec1-v2.pdf page 104
295 """
297 _fields = [
298 ('a', OctetString),
299 ('b', OctetString),
300 ('seed', OctetBitString, {'optional': True}),
301 ]
304class SpecifiedECDomain(Sequence):
305 """
306 Source: http://www.secg.org/sec1-v2.pdf page 103
307 """
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 ]
320class NamedCurve(ObjectIdentifier):
321 """
322 Various named curves
324 Original Name: None
325 Source: https://tools.ietf.org/html/rfc3279#page-23,
326 https://tools.ietf.org/html/rfc5480#page-5
327 """
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 }
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 }
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
497 :param name:
498 A unicode string of the curve name
500 :param oid:
501 A unicode string of the dotted format OID
503 :param key_size:
504 An integer of the number of bytes the private key should be
505 encoded to
506 """
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
514class ECDomainParameters(Choice):
515 """
516 Source: http://www.secg.org/sec1-v2.pdf page 102
517 """
519 _alternatives = [
520 ('specified', SpecifiedECDomain),
521 ('named', NamedCurve),
522 ('implicit_ca', Null),
523 ]
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 ))
535 if self.name == 'specified':
536 order = self.chosen['order'].native
537 return math.ceil(math.log(order, 2.0) / 8.0)
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]
551class ECPrivateKeyVersion(Integer):
552 """
553 Original Name: None
554 Source: http://www.secg.org/sec1-v2.pdf page 108
555 """
557 _map = {
558 1: 'ecPrivkeyVer1',
559 }
562class ECPrivateKey(Sequence):
563 """
564 Source: http://www.secg.org/sec1-v2.pdf page 108
565 """
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 ]
574 # Ensures the key is set to the correct length when encoding
575 _key_size = None
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)
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))
588 elif self._key_size is not None:
589 self._update_key_size()
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)
595 return res
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
601 :param key_size:
602 An integer byte length to encode the private_key to
603 """
605 self._key_size = key_size
606 self._update_key_size()
608 def _update_key_size(self):
609 """
610 Ensure the private_key explicit encoding width is set
611 """
613 if self._key_size is not None and isinstance(self['private_key'], IntegerOctetString):
614 self['private_key'].set_encoded_width(self._key_size)
617class DSAParams(Sequence):
618 """
619 Parameters for a DSA public or private key
621 Original Name: Dss-Parms
622 Source: https://tools.ietf.org/html/rfc3279#page-9
623 """
625 _fields = [
626 ('p', Integer),
627 ('q', Integer),
628 ('g', Integer),
629 ]
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 """
637 _fields = [
638 ('type', ObjectIdentifier),
639 ('values', SetOf, {'spec': Any}),
640 ]
643class Attributes(SetOf):
644 """
645 Source: https://tools.ietf.org/html/rfc5208#page-3
646 """
648 _child_spec = Attribute
651class PrivateKeyAlgorithmId(ObjectIdentifier):
652 """
653 These OIDs for various public keys are reused when storing private keys
654 inside of a PKCS#8 structure
656 Original Name: None
657 Source: https://tools.ietf.org/html/rfc3279
658 """
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 }
677class PrivateKeyAlgorithm(_ForceNullParameters, Sequence):
678 """
679 Original Name: PrivateKeyAlgorithmIdentifier
680 Source: https://tools.ietf.org/html/rfc5208#page-3
681 """
683 _fields = [
684 ('algorithm', PrivateKeyAlgorithmId),
685 ('parameters', Any, {'optional': True}),
686 ]
688 _oid_pair = ('algorithm', 'parameters')
689 _oid_specs = {
690 'dsa': DSAParams,
691 'ec': ECDomainParameters,
692 'rsassa_pss': RSASSAPSSParams,
693 }
696class PrivateKeyInfo(Sequence):
697 """
698 Source: https://tools.ietf.org/html/rfc5208#page-3
699 """
701 _fields = [
702 ('version', Integer),
703 ('private_key_algorithm', PrivateKeyAlgorithm),
704 ('private_key', ParsableOctetString),
705 ('attributes', Attributes, {'implicit': 0, 'optional': True}),
706 ]
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]
723 _spec_callbacks = {
724 'private_key': _private_key_spec
725 }
727 _algorithm = None
728 _bit_size = None
729 _public_key = None
730 _fingerprint = None
732 @classmethod
733 def wrap(cls, private_key, algorithm):
734 """
735 Wraps a private key in a PrivateKeyInfo structure
737 :param private_key:
738 A byte string or Asn1Value object of the private key
740 :param algorithm:
741 A unicode string of "rsa", "dsa" or "ec"
743 :return:
744 A PrivateKeyInfo object
745 """
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 ))
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 ))
783 private_key_algo = PrivateKeyAlgorithm()
784 private_key_algo['algorithm'] = PrivateKeyAlgorithmId(algorithm)
785 private_key_algo['parameters'] = params
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
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
798 return container
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)
805 algorithm = self['private_key_algorithm']
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)
817 return res
819 def unwrap(self):
820 """
821 Unwraps the private key into an RSAPrivateKey, DSAPrivateKey or
822 ECPrivateKey object
824 :return:
825 An RSAPrivateKey, DSAPrivateKey or ECPrivateKey object
826 """
828 raise APIException(
829 'asn1crypto.keys.PrivateKeyInfo().unwrap() has been removed, '
830 'please use oscrypto.asymmetric.PrivateKey().unwrap() instead')
832 @property
833 def curve(self):
834 """
835 Returns information about the curve used for an EC key
837 :raises:
838 ValueError - when the key is not an EC key
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 """
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 ))
856 params = self['private_key_algorithm']['parameters']
857 chosen = params.chosen
859 if params.name == 'implicit_ca':
860 value = None
861 else:
862 value = chosen.native
864 return (params.name, value)
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
872 :raises:
873 ValueError - when the key is not a DSA key
875 :return:
876 A unicode string of "sha1" or "sha2"
877 """
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 ))
888 byte_len = math.log(self['private_key_algorithm']['parameters']['q'].native, 2) / 8
890 return 'sha1' if byte_len <= 20 else 'sha2'
892 @property
893 def algorithm(self):
894 """
895 :return:
896 A unicode string of "rsa", "rsassa_pss", "dsa" or "ec"
897 """
899 if self._algorithm is None:
900 self._algorithm = self['private_key_algorithm']['algorithm'].native
901 return self._algorithm
903 @property
904 def bit_size(self):
905 """
906 :return:
907 The bit size of the private key, as an integer
908 """
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
923 @property
924 def byte_size(self):
925 """
926 :return:
927 The byte size of the private key, as an integer
928 """
930 return int(math.ceil(self.bit_size / 8))
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 """
940 raise APIException(
941 'asn1crypto.keys.PrivateKeyInfo().public_key has been removed, '
942 'please use oscrypto.asymmetric.PrivateKey().public_key.unwrap() instead')
944 @property
945 def public_key_info(self):
946 """
947 :return:
948 A PublicKeyInfo object derived from this private key.
949 """
951 raise APIException(
952 'asn1crypto.keys.PrivateKeyInfo().public_key_info has been removed, '
953 'please use oscrypto.asymmetric.PrivateKey().public_key.asn1 instead')
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.
961 This fingerprint is not compatible with fingerprints generated by any
962 other software.
964 :return:
965 A byte string that is a sha256 hash of selected components (based
966 on the key type)
967 """
969 raise APIException(
970 'asn1crypto.keys.PrivateKeyInfo().fingerprint has been removed, '
971 'please use oscrypto.asymmetric.PrivateKey().fingerprint instead')
974class EncryptedPrivateKeyInfo(Sequence):
975 """
976 Source: https://tools.ietf.org/html/rfc5208#page-4
977 """
979 _fields = [
980 ('encryption_algorithm', EncryptionAlgorithm),
981 ('encrypted_data', OctetString),
982 ]
985# These structures are from https://tools.ietf.org/html/rfc3279
987class ValidationParms(Sequence):
988 """
989 Source: https://tools.ietf.org/html/rfc3279#page-10
990 """
992 _fields = [
993 ('seed', BitString),
994 ('pgen_counter', Integer),
995 ]
998class DomainParameters(Sequence):
999 """
1000 Source: https://tools.ietf.org/html/rfc3279#page-10
1001 """
1003 _fields = [
1004 ('p', Integer),
1005 ('g', Integer),
1006 ('q', Integer),
1007 ('j', Integer, {'optional': True}),
1008 ('validation_params', ValidationParms, {'optional': True}),
1009 ]
1012class PublicKeyAlgorithmId(ObjectIdentifier):
1013 """
1014 Original Name: None
1015 Source: https://tools.ietf.org/html/rfc3279
1016 """
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 }
1039class PublicKeyAlgorithm(_ForceNullParameters, Sequence):
1040 """
1041 Original Name: AlgorithmIdentifier
1042 Source: https://tools.ietf.org/html/rfc5280#page-18
1043 """
1045 _fields = [
1046 ('algorithm', PublicKeyAlgorithmId),
1047 ('parameters', Any, {'optional': True}),
1048 ]
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 }
1060class PublicKeyInfo(Sequence):
1061 """
1062 Original Name: SubjectPublicKeyInfo
1063 Source: https://tools.ietf.org/html/rfc5280#page-17
1064 """
1066 _fields = [
1067 ('algorithm', PublicKeyAlgorithm),
1068 ('public_key', ParsableOctetBitString),
1069 ]
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]
1090 _spec_callbacks = {
1091 'public_key': _public_key_spec
1092 }
1094 _algorithm = None
1095 _bit_size = None
1096 _fingerprint = None
1097 _sha1 = None
1098 _sha256 = None
1100 @classmethod
1101 def wrap(cls, public_key, algorithm):
1102 """
1103 Wraps a public key in a PublicKeyInfo structure
1105 :param public_key:
1106 A byte string or Asn1Value object of the public key
1108 :param algorithm:
1109 A unicode string of "rsa"
1111 :return:
1112 A PublicKeyInfo object
1113 """
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 ))
1123 if algorithm != 'rsa' and algorithm != 'rsassa_pss':
1124 raise ValueError(unwrap(
1125 '''
1126 algorithm must "rsa", not %s
1127 ''',
1128 repr(algorithm)
1129 ))
1131 algo = PublicKeyAlgorithm()
1132 algo['algorithm'] = PublicKeyAlgorithmId(algorithm)
1133 algo['parameters'] = Null()
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)
1141 return container
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.
1148 :return:
1149 An RSAPublicKey object
1150 """
1152 raise APIException(
1153 'asn1crypto.keys.PublicKeyInfo().unwrap() has been removed, '
1154 'please use oscrypto.asymmetric.PublicKey().unwrap() instead')
1156 @property
1157 def curve(self):
1158 """
1159 Returns information about the curve used for an EC key
1161 :raises:
1162 ValueError - when the key is not an EC key
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 """
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 ))
1180 params = self['algorithm']['parameters']
1181 chosen = params.chosen
1183 if params.name == 'implicit_ca':
1184 value = None
1185 else:
1186 value = chosen.native
1188 return (params.name, value)
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
1196 :raises:
1197 ValueError - when the key is not a DSA key
1199 :return:
1200 A unicode string of "sha1" or "sha2" or None if no parameters are
1201 present
1202 """
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 ))
1213 parameters = self['algorithm']['parameters']
1214 if parameters.native is None:
1215 return None
1217 byte_len = math.log(parameters['q'].native, 2) / 8
1219 return 'sha1' if byte_len <= 20 else 'sha2'
1221 @property
1222 def algorithm(self):
1223 """
1224 :return:
1225 A unicode string of "rsa", "rsassa_pss", "dsa" or "ec"
1226 """
1228 if self._algorithm is None:
1229 self._algorithm = self['algorithm']['algorithm'].native
1230 return self._algorithm
1232 @property
1233 def bit_size(self):
1234 """
1235 :return:
1236 The bit size of the public key, as an integer
1237 """
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
1252 return self._bit_size
1254 @property
1255 def byte_size(self):
1256 """
1257 :return:
1258 The byte size of the public key, as an integer
1259 """
1261 return int(math.ceil(self.bit_size / 8))
1263 @property
1264 def sha1(self):
1265 """
1266 :return:
1267 The SHA1 hash of the DER-encoded bytes of this public key info
1268 """
1270 if self._sha1 is None:
1271 self._sha1 = hashlib.sha1(byte_cls(self['public_key'])).digest()
1272 return self._sha1
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 """
1281 if self._sha256 is None:
1282 self._sha256 = hashlib.sha256(byte_cls(self['public_key'])).digest()
1283 return self._sha256
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.
1291 This fingerprint is not compatible with fingerprints generated by any
1292 other software.
1294 :return:
1295 A byte string that is a sha256 hash of selected components (based
1296 on the key type)
1297 """
1299 raise APIException(
1300 'asn1crypto.keys.PublicKeyInfo().fingerprint has been removed, '
1301 'please use oscrypto.asymmetric.PublicKey().fingerprint instead')