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