• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# coding: utf-8
2
3"""
4ASN.1 type classes for various algorithms using in various aspects of public
5key cryptography. Exports the following items:
6
7 - AlgorithmIdentifier()
8 - AnyAlgorithmIdentifier()
9 - DigestAlgorithm()
10 - DigestInfo()
11 - DSASignature()
12 - EncryptionAlgorithm()
13 - HmacAlgorithm()
14 - KdfAlgorithm()
15 - Pkcs5MacAlgorithm()
16 - SignedDigestAlgorithm()
17
18Other type classes are defined that help compose the types listed above.
19"""
20
21from __future__ import unicode_literals, division, absolute_import, print_function
22
23from ._errors import unwrap
24from ._int import fill_width
25from .util import int_from_bytes, int_to_bytes
26from .core import (
27    Any,
28    Choice,
29    Integer,
30    Null,
31    ObjectIdentifier,
32    OctetString,
33    Sequence,
34    Void,
35)
36
37
38# Structures and OIDs in this file are pulled from
39# https://tools.ietf.org/html/rfc3279, https://tools.ietf.org/html/rfc4055,
40# https://tools.ietf.org/html/rfc5758, https://tools.ietf.org/html/rfc7292,
41# http://www.emc.com/collateral/white-papers/h11302-pkcs5v2-1-password-based-cryptography-standard-wp.pdf
42
43class AlgorithmIdentifier(Sequence):
44    _fields = [
45        ('algorithm', ObjectIdentifier),
46        ('parameters', Any, {'optional': True}),
47    ]
48
49
50class _ForceNullParameters(object):
51    """
52    Various structures based on AlgorithmIdentifier require that the parameters
53    field be core.Null() for certain OIDs. This mixin ensures that happens.
54    """
55
56    # The following attribute, plus the parameters spec callback and custom
57    # __setitem__ are all to handle a situation where parameters should not be
58    # optional and must be Null for certain OIDs. More info at
59    # https://tools.ietf.org/html/rfc4055#page-15 and
60    # https://tools.ietf.org/html/rfc4055#section-2.1
61    _null_algos = set([
62        '1.2.840.113549.1.1.1',    # rsassa_pkcs1v15 / rsaes_pkcs1v15 / rsa
63        '1.2.840.113549.1.1.11',   # sha256_rsa
64        '1.2.840.113549.1.1.12',   # sha384_rsa
65        '1.2.840.113549.1.1.13',   # sha512_rsa
66        '1.2.840.113549.1.1.14',   # sha224_rsa
67        '1.3.14.3.2.26',           # sha1
68        '2.16.840.1.101.3.4.2.4',  # sha224
69        '2.16.840.1.101.3.4.2.1',  # sha256
70        '2.16.840.1.101.3.4.2.2',  # sha384
71        '2.16.840.1.101.3.4.2.3',  # sha512
72    ])
73
74    def _parameters_spec(self):
75        if self._oid_pair == ('algorithm', 'parameters'):
76            algo = self['algorithm'].native
77            if algo in self._oid_specs:
78                return self._oid_specs[algo]
79
80        if self['algorithm'].dotted in self._null_algos:
81            return Null
82
83        return None
84
85    _spec_callbacks = {
86        'parameters': _parameters_spec
87    }
88
89    # We have to override this since the spec callback uses the value of
90    # algorithm to determine the parameter spec, however default values are
91    # assigned before setting a field, so a default value can't be based on
92    # another field value (unless it is a default also). Thus we have to
93    # manually check to see if the algorithm was set and parameters is unset,
94    # and then fix the value as appropriate.
95    def __setitem__(self, key, value):
96        res = super(_ForceNullParameters, self).__setitem__(key, value)
97        if key != 'algorithm':
98            return res
99        if self['algorithm'].dotted not in self._null_algos:
100            return res
101        if self['parameters'].__class__ != Void:
102            return res
103        self['parameters'] = Null()
104        return res
105
106
107class HmacAlgorithmId(ObjectIdentifier):
108    _map = {
109        '1.3.14.3.2.10': 'des_mac',
110        '1.2.840.113549.2.7': 'sha1',
111        '1.2.840.113549.2.8': 'sha224',
112        '1.2.840.113549.2.9': 'sha256',
113        '1.2.840.113549.2.10': 'sha384',
114        '1.2.840.113549.2.11': 'sha512',
115        '1.2.840.113549.2.12': 'sha512_224',
116        '1.2.840.113549.2.13': 'sha512_256',
117        '2.16.840.1.101.3.4.2.13': 'sha3_224',
118        '2.16.840.1.101.3.4.2.14': 'sha3_256',
119        '2.16.840.1.101.3.4.2.15': 'sha3_384',
120        '2.16.840.1.101.3.4.2.16': 'sha3_512',
121    }
122
123
124class HmacAlgorithm(Sequence):
125    _fields = [
126        ('algorithm', HmacAlgorithmId),
127        ('parameters', Any, {'optional': True}),
128    ]
129
130
131class DigestAlgorithmId(ObjectIdentifier):
132    _map = {
133        '1.2.840.113549.2.2': 'md2',
134        '1.2.840.113549.2.5': 'md5',
135        '1.3.14.3.2.26': 'sha1',
136        '2.16.840.1.101.3.4.2.4': 'sha224',
137        '2.16.840.1.101.3.4.2.1': 'sha256',
138        '2.16.840.1.101.3.4.2.2': 'sha384',
139        '2.16.840.1.101.3.4.2.3': 'sha512',
140        '2.16.840.1.101.3.4.2.5': 'sha512_224',
141        '2.16.840.1.101.3.4.2.6': 'sha512_256',
142        '2.16.840.1.101.3.4.2.7': 'sha3_224',
143        '2.16.840.1.101.3.4.2.8': 'sha3_256',
144        '2.16.840.1.101.3.4.2.9': 'sha3_384',
145        '2.16.840.1.101.3.4.2.10': 'sha3_512',
146        '2.16.840.1.101.3.4.2.11': 'shake128',
147        '2.16.840.1.101.3.4.2.12': 'shake256',
148        '2.16.840.1.101.3.4.2.17': 'shake128_len',
149        '2.16.840.1.101.3.4.2.18': 'shake256_len',
150    }
151
152
153class DigestAlgorithm(_ForceNullParameters, Sequence):
154    _fields = [
155        ('algorithm', DigestAlgorithmId),
156        ('parameters', Any, {'optional': True}),
157    ]
158
159
160# This structure is what is signed with a SignedDigestAlgorithm
161class DigestInfo(Sequence):
162    _fields = [
163        ('digest_algorithm', DigestAlgorithm),
164        ('digest', OctetString),
165    ]
166
167
168class MaskGenAlgorithmId(ObjectIdentifier):
169    _map = {
170        '1.2.840.113549.1.1.8': 'mgf1',
171    }
172
173
174class MaskGenAlgorithm(Sequence):
175    _fields = [
176        ('algorithm', MaskGenAlgorithmId),
177        ('parameters', Any, {'optional': True}),
178    ]
179
180    _oid_pair = ('algorithm', 'parameters')
181    _oid_specs = {
182        'mgf1': DigestAlgorithm
183    }
184
185
186class TrailerField(Integer):
187    _map = {
188        1: 'trailer_field_bc',
189    }
190
191
192class RSASSAPSSParams(Sequence):
193    _fields = [
194        (
195            'hash_algorithm',
196            DigestAlgorithm,
197            {
198                'explicit': 0,
199                'default': {'algorithm': 'sha1'},
200            }
201        ),
202        (
203            'mask_gen_algorithm',
204            MaskGenAlgorithm,
205            {
206                'explicit': 1,
207                'default': {
208                    'algorithm': 'mgf1',
209                    'parameters': {'algorithm': 'sha1'},
210                },
211            }
212        ),
213        (
214            'salt_length',
215            Integer,
216            {
217                'explicit': 2,
218                'default': 20,
219            }
220        ),
221        (
222            'trailer_field',
223            TrailerField,
224            {
225                'explicit': 3,
226                'default': 'trailer_field_bc',
227            }
228        ),
229    ]
230
231
232class SignedDigestAlgorithmId(ObjectIdentifier):
233    _map = {
234        '1.3.14.3.2.3': 'md5_rsa',
235        '1.3.14.3.2.29': 'sha1_rsa',
236        '1.3.14.7.2.3.1': 'md2_rsa',
237        '1.2.840.113549.1.1.2': 'md2_rsa',
238        '1.2.840.113549.1.1.4': 'md5_rsa',
239        '1.2.840.113549.1.1.5': 'sha1_rsa',
240        '1.2.840.113549.1.1.14': 'sha224_rsa',
241        '1.2.840.113549.1.1.11': 'sha256_rsa',
242        '1.2.840.113549.1.1.12': 'sha384_rsa',
243        '1.2.840.113549.1.1.13': 'sha512_rsa',
244        '1.2.840.113549.1.1.10': 'rsassa_pss',
245        '1.2.840.10040.4.3': 'sha1_dsa',
246        '1.3.14.3.2.13': 'sha1_dsa',
247        '1.3.14.3.2.27': 'sha1_dsa',
248        '2.16.840.1.101.3.4.3.1': 'sha224_dsa',
249        '2.16.840.1.101.3.4.3.2': 'sha256_dsa',
250        '1.2.840.10045.4.1': 'sha1_ecdsa',
251        '1.2.840.10045.4.3.1': 'sha224_ecdsa',
252        '1.2.840.10045.4.3.2': 'sha256_ecdsa',
253        '1.2.840.10045.4.3.3': 'sha384_ecdsa',
254        '1.2.840.10045.4.3.4': 'sha512_ecdsa',
255        '2.16.840.1.101.3.4.3.9': 'sha3_224_ecdsa',
256        '2.16.840.1.101.3.4.3.10': 'sha3_256_ecdsa',
257        '2.16.840.1.101.3.4.3.11': 'sha3_384_ecdsa',
258        '2.16.840.1.101.3.4.3.12': 'sha3_512_ecdsa',
259        # For when the digest is specified elsewhere in a Sequence
260        '1.2.840.113549.1.1.1': 'rsassa_pkcs1v15',
261        '1.2.840.10040.4.1': 'dsa',
262        '1.2.840.10045.4': 'ecdsa',
263        # RFC 8410 -- https://tools.ietf.org/html/rfc8410
264        '1.3.101.112': 'ed25519',
265        '1.3.101.113': 'ed448',
266    }
267
268    _reverse_map = {
269        'dsa': '1.2.840.10040.4.1',
270        'ecdsa': '1.2.840.10045.4',
271        'md2_rsa': '1.2.840.113549.1.1.2',
272        'md5_rsa': '1.2.840.113549.1.1.4',
273        'rsassa_pkcs1v15': '1.2.840.113549.1.1.1',
274        'rsassa_pss': '1.2.840.113549.1.1.10',
275        'sha1_dsa': '1.2.840.10040.4.3',
276        'sha1_ecdsa': '1.2.840.10045.4.1',
277        'sha1_rsa': '1.2.840.113549.1.1.5',
278        'sha224_dsa': '2.16.840.1.101.3.4.3.1',
279        'sha224_ecdsa': '1.2.840.10045.4.3.1',
280        'sha224_rsa': '1.2.840.113549.1.1.14',
281        'sha256_dsa': '2.16.840.1.101.3.4.3.2',
282        'sha256_ecdsa': '1.2.840.10045.4.3.2',
283        'sha256_rsa': '1.2.840.113549.1.1.11',
284        'sha384_ecdsa': '1.2.840.10045.4.3.3',
285        'sha384_rsa': '1.2.840.113549.1.1.12',
286        'sha512_ecdsa': '1.2.840.10045.4.3.4',
287        'sha512_rsa': '1.2.840.113549.1.1.13',
288        'sha3_224_ecdsa': '2.16.840.1.101.3.4.3.9',
289        'sha3_256_ecdsa': '2.16.840.1.101.3.4.3.10',
290        'sha3_384_ecdsa': '2.16.840.1.101.3.4.3.11',
291        'sha3_512_ecdsa': '2.16.840.1.101.3.4.3.12',
292        'ed25519': '1.3.101.112',
293        'ed448': '1.3.101.113',
294    }
295
296
297class SignedDigestAlgorithm(_ForceNullParameters, Sequence):
298    _fields = [
299        ('algorithm', SignedDigestAlgorithmId),
300        ('parameters', Any, {'optional': True}),
301    ]
302
303    _oid_pair = ('algorithm', 'parameters')
304    _oid_specs = {
305        'rsassa_pss': RSASSAPSSParams,
306    }
307
308    @property
309    def signature_algo(self):
310        """
311        :return:
312            A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa",
313            "ecdsa", "ed25519" or "ed448"
314        """
315
316        algorithm = self['algorithm'].native
317
318        algo_map = {
319            'md2_rsa': 'rsassa_pkcs1v15',
320            'md5_rsa': 'rsassa_pkcs1v15',
321            'sha1_rsa': 'rsassa_pkcs1v15',
322            'sha224_rsa': 'rsassa_pkcs1v15',
323            'sha256_rsa': 'rsassa_pkcs1v15',
324            'sha384_rsa': 'rsassa_pkcs1v15',
325            'sha512_rsa': 'rsassa_pkcs1v15',
326            'rsassa_pkcs1v15': 'rsassa_pkcs1v15',
327            'rsassa_pss': 'rsassa_pss',
328            'sha1_dsa': 'dsa',
329            'sha224_dsa': 'dsa',
330            'sha256_dsa': 'dsa',
331            'dsa': 'dsa',
332            'sha1_ecdsa': 'ecdsa',
333            'sha224_ecdsa': 'ecdsa',
334            'sha256_ecdsa': 'ecdsa',
335            'sha384_ecdsa': 'ecdsa',
336            'sha512_ecdsa': 'ecdsa',
337            'sha3_224_ecdsa': 'ecdsa',
338            'sha3_256_ecdsa': 'ecdsa',
339            'sha3_384_ecdsa': 'ecdsa',
340            'sha3_512_ecdsa': 'ecdsa',
341            'ecdsa': 'ecdsa',
342            'ed25519': 'ed25519',
343            'ed448': 'ed448',
344        }
345        if algorithm in algo_map:
346            return algo_map[algorithm]
347
348        raise ValueError(unwrap(
349            '''
350            Signature algorithm not known for %s
351            ''',
352            algorithm
353        ))
354
355    @property
356    def hash_algo(self):
357        """
358        :return:
359            A unicode string of "md2", "md5", "sha1", "sha224", "sha256",
360            "sha384", "sha512", "sha512_224", "sha512_256" or "shake256"
361        """
362
363        algorithm = self['algorithm'].native
364
365        algo_map = {
366            'md2_rsa': 'md2',
367            'md5_rsa': 'md5',
368            'sha1_rsa': 'sha1',
369            'sha224_rsa': 'sha224',
370            'sha256_rsa': 'sha256',
371            'sha384_rsa': 'sha384',
372            'sha512_rsa': 'sha512',
373            'sha1_dsa': 'sha1',
374            'sha224_dsa': 'sha224',
375            'sha256_dsa': 'sha256',
376            'sha1_ecdsa': 'sha1',
377            'sha224_ecdsa': 'sha224',
378            'sha256_ecdsa': 'sha256',
379            'sha384_ecdsa': 'sha384',
380            'sha512_ecdsa': 'sha512',
381            'ed25519': 'sha512',
382            'ed448': 'shake256',
383        }
384        if algorithm in algo_map:
385            return algo_map[algorithm]
386
387        if algorithm == 'rsassa_pss':
388            return self['parameters']['hash_algorithm']['algorithm'].native
389
390        raise ValueError(unwrap(
391            '''
392            Hash algorithm not known for %s
393            ''',
394            algorithm
395        ))
396
397
398class Pbkdf2Salt(Choice):
399    _alternatives = [
400        ('specified', OctetString),
401        ('other_source', AlgorithmIdentifier),
402    ]
403
404
405class Pbkdf2Params(Sequence):
406    _fields = [
407        ('salt', Pbkdf2Salt),
408        ('iteration_count', Integer),
409        ('key_length', Integer, {'optional': True}),
410        ('prf', HmacAlgorithm, {'default': {'algorithm': 'sha1'}}),
411    ]
412
413
414class KdfAlgorithmId(ObjectIdentifier):
415    _map = {
416        '1.2.840.113549.1.5.12': 'pbkdf2'
417    }
418
419
420class KdfAlgorithm(Sequence):
421    _fields = [
422        ('algorithm', KdfAlgorithmId),
423        ('parameters', Any, {'optional': True}),
424    ]
425    _oid_pair = ('algorithm', 'parameters')
426    _oid_specs = {
427        'pbkdf2': Pbkdf2Params
428    }
429
430
431class DHParameters(Sequence):
432    """
433    Original Name: DHParameter
434    Source: ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-3.asc section 9
435    """
436
437    _fields = [
438        ('p', Integer),
439        ('g', Integer),
440        ('private_value_length', Integer, {'optional': True}),
441    ]
442
443
444class KeyExchangeAlgorithmId(ObjectIdentifier):
445    _map = {
446        '1.2.840.113549.1.3.1': 'dh',
447    }
448
449
450class KeyExchangeAlgorithm(Sequence):
451    _fields = [
452        ('algorithm', KeyExchangeAlgorithmId),
453        ('parameters', Any, {'optional': True}),
454    ]
455    _oid_pair = ('algorithm', 'parameters')
456    _oid_specs = {
457        'dh': DHParameters,
458    }
459
460
461class Rc2Params(Sequence):
462    _fields = [
463        ('rc2_parameter_version', Integer, {'optional': True}),
464        ('iv', OctetString),
465    ]
466
467
468class Rc5ParamVersion(Integer):
469    _map = {
470        16: 'v1-0'
471    }
472
473
474class Rc5Params(Sequence):
475    _fields = [
476        ('version', Rc5ParamVersion),
477        ('rounds', Integer),
478        ('block_size_in_bits', Integer),
479        ('iv', OctetString, {'optional': True}),
480    ]
481
482
483class Pbes1Params(Sequence):
484    _fields = [
485        ('salt', OctetString),
486        ('iterations', Integer),
487    ]
488
489
490class CcmParams(Sequence):
491    # https://tools.ietf.org/html/rfc5084
492    # aes_ICVlen: 4 | 6 | 8 | 10 | 12 | 14 | 16
493    _fields = [
494        ('aes_nonce', OctetString),
495        ('aes_icvlen', Integer),
496    ]
497
498
499class PSourceAlgorithmId(ObjectIdentifier):
500    _map = {
501        '1.2.840.113549.1.1.9': 'p_specified',
502    }
503
504
505class PSourceAlgorithm(Sequence):
506    _fields = [
507        ('algorithm', PSourceAlgorithmId),
508        ('parameters', Any, {'optional': True}),
509    ]
510
511    _oid_pair = ('algorithm', 'parameters')
512    _oid_specs = {
513        'p_specified': OctetString
514    }
515
516
517class RSAESOAEPParams(Sequence):
518    _fields = [
519        (
520            'hash_algorithm',
521            DigestAlgorithm,
522            {
523                'explicit': 0,
524                'default': {'algorithm': 'sha1'}
525            }
526        ),
527        (
528            'mask_gen_algorithm',
529            MaskGenAlgorithm,
530            {
531                'explicit': 1,
532                'default': {
533                    'algorithm': 'mgf1',
534                    'parameters': {'algorithm': 'sha1'}
535                }
536            }
537        ),
538        (
539            'p_source_algorithm',
540            PSourceAlgorithm,
541            {
542                'explicit': 2,
543                'default': {
544                    'algorithm': 'p_specified',
545                    'parameters': b''
546                }
547            }
548        ),
549    ]
550
551
552class DSASignature(Sequence):
553    """
554    An ASN.1 class for translating between the OS crypto library's
555    representation of an (EC)DSA signature and the ASN.1 structure that is part
556    of various RFCs.
557
558    Original Name: DSS-Sig-Value
559    Source: https://tools.ietf.org/html/rfc3279#section-2.2.2
560    """
561
562    _fields = [
563        ('r', Integer),
564        ('s', Integer),
565    ]
566
567    @classmethod
568    def from_p1363(cls, data):
569        """
570        Reads a signature from a byte string encoding accordint to IEEE P1363,
571        which is used by Microsoft's BCryptSignHash() function.
572
573        :param data:
574            A byte string from BCryptSignHash()
575
576        :return:
577            A DSASignature object
578        """
579
580        r = int_from_bytes(data[0:len(data) // 2])
581        s = int_from_bytes(data[len(data) // 2:])
582        return cls({'r': r, 's': s})
583
584    def to_p1363(self):
585        """
586        Dumps a signature to a byte string compatible with Microsoft's
587        BCryptVerifySignature() function.
588
589        :return:
590            A byte string compatible with BCryptVerifySignature()
591        """
592
593        r_bytes = int_to_bytes(self['r'].native)
594        s_bytes = int_to_bytes(self['s'].native)
595
596        int_byte_length = max(len(r_bytes), len(s_bytes))
597        r_bytes = fill_width(r_bytes, int_byte_length)
598        s_bytes = fill_width(s_bytes, int_byte_length)
599
600        return r_bytes + s_bytes
601
602
603class EncryptionAlgorithmId(ObjectIdentifier):
604    _map = {
605        '1.3.14.3.2.7': 'des',
606        '1.2.840.113549.3.7': 'tripledes_3key',
607        '1.2.840.113549.3.2': 'rc2',
608        '1.2.840.113549.3.4': 'rc4',
609        '1.2.840.113549.3.9': 'rc5',
610        # From http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html#AES
611        '2.16.840.1.101.3.4.1.1': 'aes128_ecb',
612        '2.16.840.1.101.3.4.1.2': 'aes128_cbc',
613        '2.16.840.1.101.3.4.1.3': 'aes128_ofb',
614        '2.16.840.1.101.3.4.1.4': 'aes128_cfb',
615        '2.16.840.1.101.3.4.1.5': 'aes128_wrap',
616        '2.16.840.1.101.3.4.1.6': 'aes128_gcm',
617        '2.16.840.1.101.3.4.1.7': 'aes128_ccm',
618        '2.16.840.1.101.3.4.1.8': 'aes128_wrap_pad',
619        '2.16.840.1.101.3.4.1.21': 'aes192_ecb',
620        '2.16.840.1.101.3.4.1.22': 'aes192_cbc',
621        '2.16.840.1.101.3.4.1.23': 'aes192_ofb',
622        '2.16.840.1.101.3.4.1.24': 'aes192_cfb',
623        '2.16.840.1.101.3.4.1.25': 'aes192_wrap',
624        '2.16.840.1.101.3.4.1.26': 'aes192_gcm',
625        '2.16.840.1.101.3.4.1.27': 'aes192_ccm',
626        '2.16.840.1.101.3.4.1.28': 'aes192_wrap_pad',
627        '2.16.840.1.101.3.4.1.41': 'aes256_ecb',
628        '2.16.840.1.101.3.4.1.42': 'aes256_cbc',
629        '2.16.840.1.101.3.4.1.43': 'aes256_ofb',
630        '2.16.840.1.101.3.4.1.44': 'aes256_cfb',
631        '2.16.840.1.101.3.4.1.45': 'aes256_wrap',
632        '2.16.840.1.101.3.4.1.46': 'aes256_gcm',
633        '2.16.840.1.101.3.4.1.47': 'aes256_ccm',
634        '2.16.840.1.101.3.4.1.48': 'aes256_wrap_pad',
635        # From PKCS#5
636        '1.2.840.113549.1.5.13': 'pbes2',
637        '1.2.840.113549.1.5.1': 'pbes1_md2_des',
638        '1.2.840.113549.1.5.3': 'pbes1_md5_des',
639        '1.2.840.113549.1.5.4': 'pbes1_md2_rc2',
640        '1.2.840.113549.1.5.6': 'pbes1_md5_rc2',
641        '1.2.840.113549.1.5.10': 'pbes1_sha1_des',
642        '1.2.840.113549.1.5.11': 'pbes1_sha1_rc2',
643        # From PKCS#12
644        '1.2.840.113549.1.12.1.1': 'pkcs12_sha1_rc4_128',
645        '1.2.840.113549.1.12.1.2': 'pkcs12_sha1_rc4_40',
646        '1.2.840.113549.1.12.1.3': 'pkcs12_sha1_tripledes_3key',
647        '1.2.840.113549.1.12.1.4': 'pkcs12_sha1_tripledes_2key',
648        '1.2.840.113549.1.12.1.5': 'pkcs12_sha1_rc2_128',
649        '1.2.840.113549.1.12.1.6': 'pkcs12_sha1_rc2_40',
650        # PKCS#1 v2.2
651        '1.2.840.113549.1.1.1': 'rsaes_pkcs1v15',
652        '1.2.840.113549.1.1.7': 'rsaes_oaep',
653    }
654
655
656class EncryptionAlgorithm(_ForceNullParameters, Sequence):
657    _fields = [
658        ('algorithm', EncryptionAlgorithmId),
659        ('parameters', Any, {'optional': True}),
660    ]
661
662    _oid_pair = ('algorithm', 'parameters')
663    _oid_specs = {
664        'des': OctetString,
665        'tripledes_3key': OctetString,
666        'rc2': Rc2Params,
667        'rc5': Rc5Params,
668        'aes128_cbc': OctetString,
669        'aes192_cbc': OctetString,
670        'aes256_cbc': OctetString,
671        'aes128_ofb': OctetString,
672        'aes192_ofb': OctetString,
673        'aes256_ofb': OctetString,
674        # From RFC5084
675        'aes128_ccm': CcmParams,
676        'aes192_ccm': CcmParams,
677        'aes256_ccm': CcmParams,
678        # From PKCS#5
679        'pbes1_md2_des': Pbes1Params,
680        'pbes1_md5_des': Pbes1Params,
681        'pbes1_md2_rc2': Pbes1Params,
682        'pbes1_md5_rc2': Pbes1Params,
683        'pbes1_sha1_des': Pbes1Params,
684        'pbes1_sha1_rc2': Pbes1Params,
685        # From PKCS#12
686        'pkcs12_sha1_rc4_128': Pbes1Params,
687        'pkcs12_sha1_rc4_40': Pbes1Params,
688        'pkcs12_sha1_tripledes_3key': Pbes1Params,
689        'pkcs12_sha1_tripledes_2key': Pbes1Params,
690        'pkcs12_sha1_rc2_128': Pbes1Params,
691        'pkcs12_sha1_rc2_40': Pbes1Params,
692        # PKCS#1 v2.2
693        'rsaes_oaep': RSAESOAEPParams,
694    }
695
696    @property
697    def kdf(self):
698        """
699        Returns the name of the key derivation function to use.
700
701        :return:
702            A unicode from of one of the following: "pbkdf1", "pbkdf2",
703            "pkcs12_kdf"
704        """
705
706        encryption_algo = self['algorithm'].native
707
708        if encryption_algo == 'pbes2':
709            return self['parameters']['key_derivation_func']['algorithm'].native
710
711        if encryption_algo.find('.') == -1:
712            if encryption_algo.find('_') != -1:
713                encryption_algo, _ = encryption_algo.split('_', 1)
714
715                if encryption_algo == 'pbes1':
716                    return 'pbkdf1'
717
718                if encryption_algo == 'pkcs12':
719                    return 'pkcs12_kdf'
720
721            raise ValueError(unwrap(
722                '''
723                Encryption algorithm "%s" does not have a registered key
724                derivation function
725                ''',
726                encryption_algo
727            ))
728
729        raise ValueError(unwrap(
730            '''
731            Unrecognized encryption algorithm "%s", can not determine key
732            derivation function
733            ''',
734            encryption_algo
735        ))
736
737    @property
738    def kdf_hmac(self):
739        """
740        Returns the HMAC algorithm to use with the KDF.
741
742        :return:
743            A unicode string of one of the following: "md2", "md5", "sha1",
744            "sha224", "sha256", "sha384", "sha512"
745        """
746
747        encryption_algo = self['algorithm'].native
748
749        if encryption_algo == 'pbes2':
750            return self['parameters']['key_derivation_func']['parameters']['prf']['algorithm'].native
751
752        if encryption_algo.find('.') == -1:
753            if encryption_algo.find('_') != -1:
754                _, hmac_algo, _ = encryption_algo.split('_', 2)
755                return hmac_algo
756
757            raise ValueError(unwrap(
758                '''
759                Encryption algorithm "%s" does not have a registered key
760                derivation function
761                ''',
762                encryption_algo
763            ))
764
765        raise ValueError(unwrap(
766            '''
767            Unrecognized encryption algorithm "%s", can not determine key
768            derivation hmac algorithm
769            ''',
770            encryption_algo
771        ))
772
773    @property
774    def kdf_salt(self):
775        """
776        Returns the byte string to use as the salt for the KDF.
777
778        :return:
779            A byte string
780        """
781
782        encryption_algo = self['algorithm'].native
783
784        if encryption_algo == 'pbes2':
785            salt = self['parameters']['key_derivation_func']['parameters']['salt']
786
787            if salt.name == 'other_source':
788                raise ValueError(unwrap(
789                    '''
790                    Can not determine key derivation salt - the
791                    reserved-for-future-use other source salt choice was
792                    specified in the PBKDF2 params structure
793                    '''
794                ))
795
796            return salt.native
797
798        if encryption_algo.find('.') == -1:
799            if encryption_algo.find('_') != -1:
800                return self['parameters']['salt'].native
801
802            raise ValueError(unwrap(
803                '''
804                Encryption algorithm "%s" does not have a registered key
805                derivation function
806                ''',
807                encryption_algo
808            ))
809
810        raise ValueError(unwrap(
811            '''
812            Unrecognized encryption algorithm "%s", can not determine key
813            derivation salt
814            ''',
815            encryption_algo
816        ))
817
818    @property
819    def kdf_iterations(self):
820        """
821        Returns the number of iterations that should be run via the KDF.
822
823        :return:
824            An integer
825        """
826
827        encryption_algo = self['algorithm'].native
828
829        if encryption_algo == 'pbes2':
830            return self['parameters']['key_derivation_func']['parameters']['iteration_count'].native
831
832        if encryption_algo.find('.') == -1:
833            if encryption_algo.find('_') != -1:
834                return self['parameters']['iterations'].native
835
836            raise ValueError(unwrap(
837                '''
838                Encryption algorithm "%s" does not have a registered key
839                derivation function
840                ''',
841                encryption_algo
842            ))
843
844        raise ValueError(unwrap(
845            '''
846            Unrecognized encryption algorithm "%s", can not determine key
847            derivation iterations
848            ''',
849            encryption_algo
850        ))
851
852    @property
853    def key_length(self):
854        """
855        Returns the key length to pass to the cipher/kdf. The PKCS#5 spec does
856        not specify a way to store the RC5 key length, however this tends not
857        to be a problem since OpenSSL does not support RC5 in PKCS#8 and OS X
858        does not provide an RC5 cipher for use in the Security Transforms
859        library.
860
861        :raises:
862            ValueError - when the key length can not be determined
863
864        :return:
865            An integer representing the length in bytes
866        """
867
868        encryption_algo = self['algorithm'].native
869
870        if encryption_algo[0:3] == 'aes':
871            return {
872                'aes128_': 16,
873                'aes192_': 24,
874                'aes256_': 32,
875            }[encryption_algo[0:7]]
876
877        cipher_lengths = {
878            'des': 8,
879            'tripledes_3key': 24,
880        }
881
882        if encryption_algo in cipher_lengths:
883            return cipher_lengths[encryption_algo]
884
885        if encryption_algo == 'rc2':
886            rc2_parameter_version = self['parameters']['rc2_parameter_version'].native
887
888            # See page 24 of
889            # http://www.emc.com/collateral/white-papers/h11302-pkcs5v2-1-password-based-cryptography-standard-wp.pdf
890            encoded_key_bits_map = {
891                160: 5,   # 40-bit
892                120: 8,   # 64-bit
893                58: 16,   # 128-bit
894            }
895
896            if rc2_parameter_version in encoded_key_bits_map:
897                return encoded_key_bits_map[rc2_parameter_version]
898
899            if rc2_parameter_version >= 256:
900                return rc2_parameter_version
901
902            if rc2_parameter_version is None:
903                return 4  # 32-bit default
904
905            raise ValueError(unwrap(
906                '''
907                Invalid RC2 parameter version found in EncryptionAlgorithm
908                parameters
909                '''
910            ))
911
912        if encryption_algo == 'pbes2':
913            key_length = self['parameters']['key_derivation_func']['parameters']['key_length'].native
914            if key_length is not None:
915                return key_length
916
917            # If the KDF params don't specify the key size, we can infer it from
918            # the encryption scheme for all schemes except for RC5. However, in
919            # practical terms, neither OpenSSL or OS X support RC5 for PKCS#8
920            # so it is unlikely to be an issue that is run into.
921
922            return self['parameters']['encryption_scheme'].key_length
923
924        if encryption_algo.find('.') == -1:
925            return {
926                'pbes1_md2_des': 8,
927                'pbes1_md5_des': 8,
928                'pbes1_md2_rc2': 8,
929                'pbes1_md5_rc2': 8,
930                'pbes1_sha1_des': 8,
931                'pbes1_sha1_rc2': 8,
932                'pkcs12_sha1_rc4_128': 16,
933                'pkcs12_sha1_rc4_40': 5,
934                'pkcs12_sha1_tripledes_3key': 24,
935                'pkcs12_sha1_tripledes_2key': 16,
936                'pkcs12_sha1_rc2_128': 16,
937                'pkcs12_sha1_rc2_40': 5,
938            }[encryption_algo]
939
940        raise ValueError(unwrap(
941            '''
942            Unrecognized encryption algorithm "%s"
943            ''',
944            encryption_algo
945        ))
946
947    @property
948    def encryption_mode(self):
949        """
950        Returns the name of the encryption mode to use.
951
952        :return:
953            A unicode string from one of the following: "cbc", "ecb", "ofb",
954            "cfb", "wrap", "gcm", "ccm", "wrap_pad"
955        """
956
957        encryption_algo = self['algorithm'].native
958
959        if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
960            return encryption_algo[7:]
961
962        if encryption_algo[0:6] == 'pbes1_':
963            return 'cbc'
964
965        if encryption_algo[0:7] == 'pkcs12_':
966            return 'cbc'
967
968        if encryption_algo in set(['des', 'tripledes_3key', 'rc2', 'rc5']):
969            return 'cbc'
970
971        if encryption_algo == 'pbes2':
972            return self['parameters']['encryption_scheme'].encryption_mode
973
974        raise ValueError(unwrap(
975            '''
976            Unrecognized encryption algorithm "%s"
977            ''',
978            encryption_algo
979        ))
980
981    @property
982    def encryption_cipher(self):
983        """
984        Returns the name of the symmetric encryption cipher to use. The key
985        length can be retrieved via the .key_length property to disabiguate
986        between different variations of TripleDES, AES, and the RC* ciphers.
987
988        :return:
989            A unicode string from one of the following: "rc2", "rc5", "des",
990            "tripledes", "aes"
991        """
992
993        encryption_algo = self['algorithm'].native
994
995        if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
996            return 'aes'
997
998        if encryption_algo in set(['des', 'rc2', 'rc5']):
999            return encryption_algo
1000
1001        if encryption_algo == 'tripledes_3key':
1002            return 'tripledes'
1003
1004        if encryption_algo == 'pbes2':
1005            return self['parameters']['encryption_scheme'].encryption_cipher
1006
1007        if encryption_algo.find('.') == -1:
1008            return {
1009                'pbes1_md2_des': 'des',
1010                'pbes1_md5_des': 'des',
1011                'pbes1_md2_rc2': 'rc2',
1012                'pbes1_md5_rc2': 'rc2',
1013                'pbes1_sha1_des': 'des',
1014                'pbes1_sha1_rc2': 'rc2',
1015                'pkcs12_sha1_rc4_128': 'rc4',
1016                'pkcs12_sha1_rc4_40': 'rc4',
1017                'pkcs12_sha1_tripledes_3key': 'tripledes',
1018                'pkcs12_sha1_tripledes_2key': 'tripledes',
1019                'pkcs12_sha1_rc2_128': 'rc2',
1020                'pkcs12_sha1_rc2_40': 'rc2',
1021            }[encryption_algo]
1022
1023        raise ValueError(unwrap(
1024            '''
1025            Unrecognized encryption algorithm "%s"
1026            ''',
1027            encryption_algo
1028        ))
1029
1030    @property
1031    def encryption_block_size(self):
1032        """
1033        Returns the block size of the encryption cipher, in bytes.
1034
1035        :return:
1036            An integer that is the block size in bytes
1037        """
1038
1039        encryption_algo = self['algorithm'].native
1040
1041        if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
1042            return 16
1043
1044        cipher_map = {
1045            'des': 8,
1046            'tripledes_3key': 8,
1047            'rc2': 8,
1048        }
1049        if encryption_algo in cipher_map:
1050            return cipher_map[encryption_algo]
1051
1052        if encryption_algo == 'rc5':
1053            return self['parameters']['block_size_in_bits'].native // 8
1054
1055        if encryption_algo == 'pbes2':
1056            return self['parameters']['encryption_scheme'].encryption_block_size
1057
1058        if encryption_algo.find('.') == -1:
1059            return {
1060                'pbes1_md2_des': 8,
1061                'pbes1_md5_des': 8,
1062                'pbes1_md2_rc2': 8,
1063                'pbes1_md5_rc2': 8,
1064                'pbes1_sha1_des': 8,
1065                'pbes1_sha1_rc2': 8,
1066                'pkcs12_sha1_rc4_128': 0,
1067                'pkcs12_sha1_rc4_40': 0,
1068                'pkcs12_sha1_tripledes_3key': 8,
1069                'pkcs12_sha1_tripledes_2key': 8,
1070                'pkcs12_sha1_rc2_128': 8,
1071                'pkcs12_sha1_rc2_40': 8,
1072            }[encryption_algo]
1073
1074        raise ValueError(unwrap(
1075            '''
1076            Unrecognized encryption algorithm "%s"
1077            ''',
1078            encryption_algo
1079        ))
1080
1081    @property
1082    def encryption_iv(self):
1083        """
1084        Returns the byte string of the initialization vector for the encryption
1085        scheme. Only the PBES2 stores the IV in the params. For PBES1, the IV
1086        is derived from the KDF and this property will return None.
1087
1088        :return:
1089            A byte string or None
1090        """
1091
1092        encryption_algo = self['algorithm'].native
1093
1094        if encryption_algo in set(['rc2', 'rc5']):
1095            return self['parameters']['iv'].native
1096
1097        # For DES/Triple DES and AES the IV is the entirety of the parameters
1098        octet_string_iv_oids = set([
1099            'des',
1100            'tripledes_3key',
1101            'aes128_cbc',
1102            'aes192_cbc',
1103            'aes256_cbc',
1104            'aes128_ofb',
1105            'aes192_ofb',
1106            'aes256_ofb',
1107        ])
1108        if encryption_algo in octet_string_iv_oids:
1109            return self['parameters'].native
1110
1111        if encryption_algo == 'pbes2':
1112            return self['parameters']['encryption_scheme'].encryption_iv
1113
1114        # All of the PBES1 algos use their KDF to create the IV. For the pbkdf1,
1115        # the KDF is told to generate a key that is an extra 8 bytes long, and
1116        # that is used for the IV. For the PKCS#12 KDF, it is called with an id
1117        # of 2 to generate the IV. In either case, we can't return the IV
1118        # without knowing the user's password.
1119        if encryption_algo.find('.') == -1:
1120            return None
1121
1122        raise ValueError(unwrap(
1123            '''
1124            Unrecognized encryption algorithm "%s"
1125            ''',
1126            encryption_algo
1127        ))
1128
1129
1130class Pbes2Params(Sequence):
1131    _fields = [
1132        ('key_derivation_func', KdfAlgorithm),
1133        ('encryption_scheme', EncryptionAlgorithm),
1134    ]
1135
1136
1137class Pbmac1Params(Sequence):
1138    _fields = [
1139        ('key_derivation_func', KdfAlgorithm),
1140        ('message_auth_scheme', HmacAlgorithm),
1141    ]
1142
1143
1144class Pkcs5MacId(ObjectIdentifier):
1145    _map = {
1146        '1.2.840.113549.1.5.14': 'pbmac1',
1147    }
1148
1149
1150class Pkcs5MacAlgorithm(Sequence):
1151    _fields = [
1152        ('algorithm', Pkcs5MacId),
1153        ('parameters', Any),
1154    ]
1155
1156    _oid_pair = ('algorithm', 'parameters')
1157    _oid_specs = {
1158        'pbmac1': Pbmac1Params,
1159    }
1160
1161
1162EncryptionAlgorithm._oid_specs['pbes2'] = Pbes2Params
1163
1164
1165class AnyAlgorithmId(ObjectIdentifier):
1166    _map = {}
1167
1168    def _setup(self):
1169        _map = self.__class__._map
1170        for other_cls in (EncryptionAlgorithmId, SignedDigestAlgorithmId, DigestAlgorithmId):
1171            for oid, name in other_cls._map.items():
1172                _map[oid] = name
1173
1174
1175class AnyAlgorithmIdentifier(_ForceNullParameters, Sequence):
1176    _fields = [
1177        ('algorithm', AnyAlgorithmId),
1178        ('parameters', Any, {'optional': True}),
1179    ]
1180
1181    _oid_pair = ('algorithm', 'parameters')
1182    _oid_specs = {}
1183
1184    def _setup(self):
1185        Sequence._setup(self)
1186        specs = self.__class__._oid_specs
1187        for other_cls in (EncryptionAlgorithm, SignedDigestAlgorithm):
1188            for oid, spec in other_cls._oid_specs.items():
1189                specs[oid] = spec
1190