• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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