• 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    }
264
265    _reverse_map = {
266        'dsa': '1.2.840.10040.4.1',
267        'ecdsa': '1.2.840.10045.4',
268        'md2_rsa': '1.2.840.113549.1.1.2',
269        'md5_rsa': '1.2.840.113549.1.1.4',
270        'rsassa_pkcs1v15': '1.2.840.113549.1.1.1',
271        'rsassa_pss': '1.2.840.113549.1.1.10',
272        'sha1_dsa': '1.2.840.10040.4.3',
273        'sha1_ecdsa': '1.2.840.10045.4.1',
274        'sha1_rsa': '1.2.840.113549.1.1.5',
275        'sha224_dsa': '2.16.840.1.101.3.4.3.1',
276        'sha224_ecdsa': '1.2.840.10045.4.3.1',
277        'sha224_rsa': '1.2.840.113549.1.1.14',
278        'sha256_dsa': '2.16.840.1.101.3.4.3.2',
279        'sha256_ecdsa': '1.2.840.10045.4.3.2',
280        'sha256_rsa': '1.2.840.113549.1.1.11',
281        'sha384_ecdsa': '1.2.840.10045.4.3.3',
282        'sha384_rsa': '1.2.840.113549.1.1.12',
283        'sha512_ecdsa': '1.2.840.10045.4.3.4',
284        'sha512_rsa': '1.2.840.113549.1.1.13',
285        'sha3_224_ecdsa': '2.16.840.1.101.3.4.3.9',
286        'sha3_256_ecdsa': '2.16.840.1.101.3.4.3.10',
287        'sha3_384_ecdsa': '2.16.840.1.101.3.4.3.11',
288        'sha3_512_ecdsa': '2.16.840.1.101.3.4.3.12',
289    }
290
291
292class SignedDigestAlgorithm(_ForceNullParameters, Sequence):
293    _fields = [
294        ('algorithm', SignedDigestAlgorithmId),
295        ('parameters', Any, {'optional': True}),
296    ]
297
298    _oid_pair = ('algorithm', 'parameters')
299    _oid_specs = {
300        'rsassa_pss': RSASSAPSSParams,
301    }
302
303    @property
304    def signature_algo(self):
305        """
306        :return:
307            A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa" or
308            "ecdsa"
309        """
310
311        algorithm = self['algorithm'].native
312
313        algo_map = {
314            'md2_rsa': 'rsassa_pkcs1v15',
315            'md5_rsa': 'rsassa_pkcs1v15',
316            'sha1_rsa': 'rsassa_pkcs1v15',
317            'sha224_rsa': 'rsassa_pkcs1v15',
318            'sha256_rsa': 'rsassa_pkcs1v15',
319            'sha384_rsa': 'rsassa_pkcs1v15',
320            'sha512_rsa': 'rsassa_pkcs1v15',
321            'rsassa_pkcs1v15': 'rsassa_pkcs1v15',
322            'rsassa_pss': 'rsassa_pss',
323            'sha1_dsa': 'dsa',
324            'sha224_dsa': 'dsa',
325            'sha256_dsa': 'dsa',
326            'dsa': 'dsa',
327            'sha1_ecdsa': 'ecdsa',
328            'sha224_ecdsa': 'ecdsa',
329            'sha256_ecdsa': 'ecdsa',
330            'sha384_ecdsa': 'ecdsa',
331            'sha512_ecdsa': 'ecdsa',
332            'sha3_224_ecdsa': 'ecdsa',
333            'sha3_256_ecdsa': 'ecdsa',
334            'sha3_384_ecdsa': 'ecdsa',
335            'sha3_512_ecdsa': 'ecdsa',
336            'ecdsa': 'ecdsa',
337        }
338        if algorithm in algo_map:
339            return algo_map[algorithm]
340
341        raise ValueError(unwrap(
342            '''
343            Signature algorithm not known for %s
344            ''',
345            algorithm
346        ))
347
348    @property
349    def hash_algo(self):
350        """
351        :return:
352            A unicode string of "md2", "md5", "sha1", "sha224", "sha256",
353            "sha384", "sha512", "sha512_224", "sha512_256"
354        """
355
356        algorithm = self['algorithm'].native
357
358        algo_map = {
359            'md2_rsa': 'md2',
360            'md5_rsa': 'md5',
361            'sha1_rsa': 'sha1',
362            'sha224_rsa': 'sha224',
363            'sha256_rsa': 'sha256',
364            'sha384_rsa': 'sha384',
365            'sha512_rsa': 'sha512',
366            'sha1_dsa': 'sha1',
367            'sha224_dsa': 'sha224',
368            'sha256_dsa': 'sha256',
369            'sha1_ecdsa': 'sha1',
370            'sha224_ecdsa': 'sha224',
371            'sha256_ecdsa': 'sha256',
372            'sha384_ecdsa': 'sha384',
373            'sha512_ecdsa': 'sha512',
374        }
375        if algorithm in algo_map:
376            return algo_map[algorithm]
377
378        if algorithm == 'rsassa_pss':
379            return self['parameters']['hash_algorithm']['algorithm'].native
380
381        raise ValueError(unwrap(
382            '''
383            Hash algorithm not known for %s
384            ''',
385            algorithm
386        ))
387
388
389class Pbkdf2Salt(Choice):
390    _alternatives = [
391        ('specified', OctetString),
392        ('other_source', AlgorithmIdentifier),
393    ]
394
395
396class Pbkdf2Params(Sequence):
397    _fields = [
398        ('salt', Pbkdf2Salt),
399        ('iteration_count', Integer),
400        ('key_length', Integer, {'optional': True}),
401        ('prf', HmacAlgorithm, {'default': {'algorithm': 'sha1'}}),
402    ]
403
404
405class KdfAlgorithmId(ObjectIdentifier):
406    _map = {
407        '1.2.840.113549.1.5.12': 'pbkdf2'
408    }
409
410
411class KdfAlgorithm(Sequence):
412    _fields = [
413        ('algorithm', KdfAlgorithmId),
414        ('parameters', Any, {'optional': True}),
415    ]
416    _oid_pair = ('algorithm', 'parameters')
417    _oid_specs = {
418        'pbkdf2': Pbkdf2Params
419    }
420
421
422class DHParameters(Sequence):
423    """
424    Original Name: DHParameter
425    Source: ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-3.asc section 9
426    """
427
428    _fields = [
429        ('p', Integer),
430        ('g', Integer),
431        ('private_value_length', Integer, {'optional': True}),
432    ]
433
434
435class KeyExchangeAlgorithmId(ObjectIdentifier):
436    _map = {
437        '1.2.840.113549.1.3.1': 'dh',
438    }
439
440
441class KeyExchangeAlgorithm(Sequence):
442    _fields = [
443        ('algorithm', KeyExchangeAlgorithmId),
444        ('parameters', Any, {'optional': True}),
445    ]
446    _oid_pair = ('algorithm', 'parameters')
447    _oid_specs = {
448        'dh': DHParameters,
449    }
450
451
452class Rc2Params(Sequence):
453    _fields = [
454        ('rc2_parameter_version', Integer, {'optional': True}),
455        ('iv', OctetString),
456    ]
457
458
459class Rc5ParamVersion(Integer):
460    _map = {
461        16: 'v1-0'
462    }
463
464
465class Rc5Params(Sequence):
466    _fields = [
467        ('version', Rc5ParamVersion),
468        ('rounds', Integer),
469        ('block_size_in_bits', Integer),
470        ('iv', OctetString, {'optional': True}),
471    ]
472
473
474class Pbes1Params(Sequence):
475    _fields = [
476        ('salt', OctetString),
477        ('iterations', Integer),
478    ]
479
480
481class CcmParams(Sequence):
482    # https://tools.ietf.org/html/rfc5084
483    # aes_ICVlen: 4 | 6 | 8 | 10 | 12 | 14 | 16
484    _fields = [
485        ('aes_nonce', OctetString),
486        ('aes_icvlen', Integer),
487    ]
488
489
490class PSourceAlgorithmId(ObjectIdentifier):
491    _map = {
492        '1.2.840.113549.1.1.9': 'p_specified',
493    }
494
495
496class PSourceAlgorithm(Sequence):
497    _fields = [
498        ('algorithm', PSourceAlgorithmId),
499        ('parameters', Any, {'optional': True}),
500    ]
501
502    _oid_pair = ('algorithm', 'parameters')
503    _oid_specs = {
504        'p_specified': OctetString
505    }
506
507
508class RSAESOAEPParams(Sequence):
509    _fields = [
510        (
511            'hash_algorithm',
512            DigestAlgorithm,
513            {
514                'explicit': 0,
515                'default': {'algorithm': 'sha1'}
516            }
517        ),
518        (
519            'mask_gen_algorithm',
520            MaskGenAlgorithm,
521            {
522                'explicit': 1,
523                'default': {
524                    'algorithm': 'mgf1',
525                    'parameters': {'algorithm': 'sha1'}
526                }
527            }
528        ),
529        (
530            'p_source_algorithm',
531            PSourceAlgorithm,
532            {
533                'explicit': 2,
534                'default': {
535                    'algorithm': 'p_specified',
536                    'parameters': b''
537                }
538            }
539        ),
540    ]
541
542
543class DSASignature(Sequence):
544    """
545    An ASN.1 class for translating between the OS crypto library's
546    representation of an (EC)DSA signature and the ASN.1 structure that is part
547    of various RFCs.
548
549    Original Name: DSS-Sig-Value
550    Source: https://tools.ietf.org/html/rfc3279#section-2.2.2
551    """
552
553    _fields = [
554        ('r', Integer),
555        ('s', Integer),
556    ]
557
558    @classmethod
559    def from_p1363(cls, data):
560        """
561        Reads a signature from a byte string encoding accordint to IEEE P1363,
562        which is used by Microsoft's BCryptSignHash() function.
563
564        :param data:
565            A byte string from BCryptSignHash()
566
567        :return:
568            A DSASignature object
569        """
570
571        r = int_from_bytes(data[0:len(data) // 2])
572        s = int_from_bytes(data[len(data) // 2:])
573        return cls({'r': r, 's': s})
574
575    def to_p1363(self):
576        """
577        Dumps a signature to a byte string compatible with Microsoft's
578        BCryptVerifySignature() function.
579
580        :return:
581            A byte string compatible with BCryptVerifySignature()
582        """
583
584        r_bytes = int_to_bytes(self['r'].native)
585        s_bytes = int_to_bytes(self['s'].native)
586
587        int_byte_length = max(len(r_bytes), len(s_bytes))
588        r_bytes = fill_width(r_bytes, int_byte_length)
589        s_bytes = fill_width(s_bytes, int_byte_length)
590
591        return r_bytes + s_bytes
592
593
594class EncryptionAlgorithmId(ObjectIdentifier):
595    _map = {
596        '1.3.14.3.2.7': 'des',
597        '1.2.840.113549.3.7': 'tripledes_3key',
598        '1.2.840.113549.3.2': 'rc2',
599        '1.2.840.113549.3.4': 'rc4',
600        '1.2.840.113549.3.9': 'rc5',
601        # From http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html#AES
602        '2.16.840.1.101.3.4.1.1': 'aes128_ecb',
603        '2.16.840.1.101.3.4.1.2': 'aes128_cbc',
604        '2.16.840.1.101.3.4.1.3': 'aes128_ofb',
605        '2.16.840.1.101.3.4.1.4': 'aes128_cfb',
606        '2.16.840.1.101.3.4.1.5': 'aes128_wrap',
607        '2.16.840.1.101.3.4.1.6': 'aes128_gcm',
608        '2.16.840.1.101.3.4.1.7': 'aes128_ccm',
609        '2.16.840.1.101.3.4.1.8': 'aes128_wrap_pad',
610        '2.16.840.1.101.3.4.1.21': 'aes192_ecb',
611        '2.16.840.1.101.3.4.1.22': 'aes192_cbc',
612        '2.16.840.1.101.3.4.1.23': 'aes192_ofb',
613        '2.16.840.1.101.3.4.1.24': 'aes192_cfb',
614        '2.16.840.1.101.3.4.1.25': 'aes192_wrap',
615        '2.16.840.1.101.3.4.1.26': 'aes192_gcm',
616        '2.16.840.1.101.3.4.1.27': 'aes192_ccm',
617        '2.16.840.1.101.3.4.1.28': 'aes192_wrap_pad',
618        '2.16.840.1.101.3.4.1.41': 'aes256_ecb',
619        '2.16.840.1.101.3.4.1.42': 'aes256_cbc',
620        '2.16.840.1.101.3.4.1.43': 'aes256_ofb',
621        '2.16.840.1.101.3.4.1.44': 'aes256_cfb',
622        '2.16.840.1.101.3.4.1.45': 'aes256_wrap',
623        '2.16.840.1.101.3.4.1.46': 'aes256_gcm',
624        '2.16.840.1.101.3.4.1.47': 'aes256_ccm',
625        '2.16.840.1.101.3.4.1.48': 'aes256_wrap_pad',
626        # From PKCS#5
627        '1.2.840.113549.1.5.13': 'pbes2',
628        '1.2.840.113549.1.5.1': 'pbes1_md2_des',
629        '1.2.840.113549.1.5.3': 'pbes1_md5_des',
630        '1.2.840.113549.1.5.4': 'pbes1_md2_rc2',
631        '1.2.840.113549.1.5.6': 'pbes1_md5_rc2',
632        '1.2.840.113549.1.5.10': 'pbes1_sha1_des',
633        '1.2.840.113549.1.5.11': 'pbes1_sha1_rc2',
634        # From PKCS#12
635        '1.2.840.113549.1.12.1.1': 'pkcs12_sha1_rc4_128',
636        '1.2.840.113549.1.12.1.2': 'pkcs12_sha1_rc4_40',
637        '1.2.840.113549.1.12.1.3': 'pkcs12_sha1_tripledes_3key',
638        '1.2.840.113549.1.12.1.4': 'pkcs12_sha1_tripledes_2key',
639        '1.2.840.113549.1.12.1.5': 'pkcs12_sha1_rc2_128',
640        '1.2.840.113549.1.12.1.6': 'pkcs12_sha1_rc2_40',
641        # PKCS#1 v2.2
642        '1.2.840.113549.1.1.1': 'rsaes_pkcs1v15',
643        '1.2.840.113549.1.1.7': 'rsaes_oaep',
644    }
645
646
647class EncryptionAlgorithm(_ForceNullParameters, Sequence):
648    _fields = [
649        ('algorithm', EncryptionAlgorithmId),
650        ('parameters', Any, {'optional': True}),
651    ]
652
653    _oid_pair = ('algorithm', 'parameters')
654    _oid_specs = {
655        'des': OctetString,
656        'tripledes_3key': OctetString,
657        'rc2': Rc2Params,
658        'rc5': Rc5Params,
659        'aes128_cbc': OctetString,
660        'aes192_cbc': OctetString,
661        'aes256_cbc': OctetString,
662        'aes128_ofb': OctetString,
663        'aes192_ofb': OctetString,
664        'aes256_ofb': OctetString,
665        # From RFC5084
666        'aes128_ccm': CcmParams,
667        'aes192_ccm': CcmParams,
668        'aes256_ccm': CcmParams,
669        # From PKCS#5
670        'pbes1_md2_des': Pbes1Params,
671        'pbes1_md5_des': Pbes1Params,
672        'pbes1_md2_rc2': Pbes1Params,
673        'pbes1_md5_rc2': Pbes1Params,
674        'pbes1_sha1_des': Pbes1Params,
675        'pbes1_sha1_rc2': Pbes1Params,
676        # From PKCS#12
677        'pkcs12_sha1_rc4_128': Pbes1Params,
678        'pkcs12_sha1_rc4_40': Pbes1Params,
679        'pkcs12_sha1_tripledes_3key': Pbes1Params,
680        'pkcs12_sha1_tripledes_2key': Pbes1Params,
681        'pkcs12_sha1_rc2_128': Pbes1Params,
682        'pkcs12_sha1_rc2_40': Pbes1Params,
683        # PKCS#1 v2.2
684        'rsaes_oaep': RSAESOAEPParams,
685    }
686
687    @property
688    def kdf(self):
689        """
690        Returns the name of the key derivation function to use.
691
692        :return:
693            A unicode from of one of the following: "pbkdf1", "pbkdf2",
694            "pkcs12_kdf"
695        """
696
697        encryption_algo = self['algorithm'].native
698
699        if encryption_algo == 'pbes2':
700            return self['parameters']['key_derivation_func']['algorithm'].native
701
702        if encryption_algo.find('.') == -1:
703            if encryption_algo.find('_') != -1:
704                encryption_algo, _ = encryption_algo.split('_', 1)
705
706                if encryption_algo == 'pbes1':
707                    return 'pbkdf1'
708
709                if encryption_algo == 'pkcs12':
710                    return 'pkcs12_kdf'
711
712            raise ValueError(unwrap(
713                '''
714                Encryption algorithm "%s" does not have a registered key
715                derivation function
716                ''',
717                encryption_algo
718            ))
719
720        raise ValueError(unwrap(
721            '''
722            Unrecognized encryption algorithm "%s", can not determine key
723            derivation function
724            ''',
725            encryption_algo
726        ))
727
728    @property
729    def kdf_hmac(self):
730        """
731        Returns the HMAC algorithm to use with the KDF.
732
733        :return:
734            A unicode string of one of the following: "md2", "md5", "sha1",
735            "sha224", "sha256", "sha384", "sha512"
736        """
737
738        encryption_algo = self['algorithm'].native
739
740        if encryption_algo == 'pbes2':
741            return self['parameters']['key_derivation_func']['parameters']['prf']['algorithm'].native
742
743        if encryption_algo.find('.') == -1:
744            if encryption_algo.find('_') != -1:
745                _, hmac_algo, _ = encryption_algo.split('_', 2)
746                return hmac_algo
747
748            raise ValueError(unwrap(
749                '''
750                Encryption algorithm "%s" does not have a registered key
751                derivation function
752                ''',
753                encryption_algo
754            ))
755
756        raise ValueError(unwrap(
757            '''
758            Unrecognized encryption algorithm "%s", can not determine key
759            derivation hmac algorithm
760            ''',
761            encryption_algo
762        ))
763
764    @property
765    def kdf_salt(self):
766        """
767        Returns the byte string to use as the salt for the KDF.
768
769        :return:
770            A byte string
771        """
772
773        encryption_algo = self['algorithm'].native
774
775        if encryption_algo == 'pbes2':
776            salt = self['parameters']['key_derivation_func']['parameters']['salt']
777
778            if salt.name == 'other_source':
779                raise ValueError(unwrap(
780                    '''
781                    Can not determine key derivation salt - the
782                    reserved-for-future-use other source salt choice was
783                    specified in the PBKDF2 params structure
784                    '''
785                ))
786
787            return salt.native
788
789        if encryption_algo.find('.') == -1:
790            if encryption_algo.find('_') != -1:
791                return self['parameters']['salt'].native
792
793            raise ValueError(unwrap(
794                '''
795                Encryption algorithm "%s" does not have a registered key
796                derivation function
797                ''',
798                encryption_algo
799            ))
800
801        raise ValueError(unwrap(
802            '''
803            Unrecognized encryption algorithm "%s", can not determine key
804            derivation salt
805            ''',
806            encryption_algo
807        ))
808
809    @property
810    def kdf_iterations(self):
811        """
812        Returns the number of iterations that should be run via the KDF.
813
814        :return:
815            An integer
816        """
817
818        encryption_algo = self['algorithm'].native
819
820        if encryption_algo == 'pbes2':
821            return self['parameters']['key_derivation_func']['parameters']['iteration_count'].native
822
823        if encryption_algo.find('.') == -1:
824            if encryption_algo.find('_') != -1:
825                return self['parameters']['iterations'].native
826
827            raise ValueError(unwrap(
828                '''
829                Encryption algorithm "%s" does not have a registered key
830                derivation function
831                ''',
832                encryption_algo
833            ))
834
835        raise ValueError(unwrap(
836            '''
837            Unrecognized encryption algorithm "%s", can not determine key
838            derivation iterations
839            ''',
840            encryption_algo
841        ))
842
843    @property
844    def key_length(self):
845        """
846        Returns the key length to pass to the cipher/kdf. The PKCS#5 spec does
847        not specify a way to store the RC5 key length, however this tends not
848        to be a problem since OpenSSL does not support RC5 in PKCS#8 and OS X
849        does not provide an RC5 cipher for use in the Security Transforms
850        library.
851
852        :raises:
853            ValueError - when the key length can not be determined
854
855        :return:
856            An integer representing the length in bytes
857        """
858
859        encryption_algo = self['algorithm'].native
860
861        if encryption_algo[0:3] == 'aes':
862            return {
863                'aes128_': 16,
864                'aes192_': 24,
865                'aes256_': 32,
866            }[encryption_algo[0:7]]
867
868        cipher_lengths = {
869            'des': 8,
870            'tripledes_3key': 24,
871        }
872
873        if encryption_algo in cipher_lengths:
874            return cipher_lengths[encryption_algo]
875
876        if encryption_algo == 'rc2':
877            rc2_params = self['parameters'].parsed['encryption_scheme']['parameters'].parsed
878            rc2_parameter_version = rc2_params['rc2_parameter_version'].native
879
880            # See page 24 of
881            # http://www.emc.com/collateral/white-papers/h11302-pkcs5v2-1-password-based-cryptography-standard-wp.pdf
882            encoded_key_bits_map = {
883                160: 5,   # 40-bit
884                120: 8,   # 64-bit
885                58: 16,   # 128-bit
886            }
887
888            if rc2_parameter_version in encoded_key_bits_map:
889                return encoded_key_bits_map[rc2_parameter_version]
890
891            if rc2_parameter_version >= 256:
892                return rc2_parameter_version
893
894            if rc2_parameter_version is None:
895                return 4  # 32-bit default
896
897            raise ValueError(unwrap(
898                '''
899                Invalid RC2 parameter version found in EncryptionAlgorithm
900                parameters
901                '''
902            ))
903
904        if encryption_algo == 'pbes2':
905            key_length = self['parameters']['key_derivation_func']['parameters']['key_length'].native
906            if key_length is not None:
907                return key_length
908
909            # If the KDF params don't specify the key size, we can infer it from
910            # the encryption scheme for all schemes except for RC5. However, in
911            # practical terms, neither OpenSSL or OS X support RC5 for PKCS#8
912            # so it is unlikely to be an issue that is run into.
913
914            return self['parameters']['encryption_scheme'].key_length
915
916        if encryption_algo.find('.') == -1:
917            return {
918                'pbes1_md2_des': 8,
919                'pbes1_md5_des': 8,
920                'pbes1_md2_rc2': 8,
921                'pbes1_md5_rc2': 8,
922                'pbes1_sha1_des': 8,
923                'pbes1_sha1_rc2': 8,
924                'pkcs12_sha1_rc4_128': 16,
925                'pkcs12_sha1_rc4_40': 5,
926                'pkcs12_sha1_tripledes_3key': 24,
927                'pkcs12_sha1_tripledes_2key': 16,
928                'pkcs12_sha1_rc2_128': 16,
929                'pkcs12_sha1_rc2_40': 5,
930            }[encryption_algo]
931
932        raise ValueError(unwrap(
933            '''
934            Unrecognized encryption algorithm "%s"
935            ''',
936            encryption_algo
937        ))
938
939    @property
940    def encryption_mode(self):
941        """
942        Returns the name of the encryption mode to use.
943
944        :return:
945            A unicode string from one of the following: "cbc", "ecb", "ofb",
946            "cfb", "wrap", "gcm", "ccm", "wrap_pad"
947        """
948
949        encryption_algo = self['algorithm'].native
950
951        if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
952            return encryption_algo[7:]
953
954        if encryption_algo[0:6] == 'pbes1_':
955            return 'cbc'
956
957        if encryption_algo[0:7] == 'pkcs12_':
958            return 'cbc'
959
960        if encryption_algo in set(['des', 'tripledes_3key', 'rc2', 'rc5']):
961            return 'cbc'
962
963        if encryption_algo == 'pbes2':
964            return self['parameters']['encryption_scheme'].encryption_mode
965
966        raise ValueError(unwrap(
967            '''
968            Unrecognized encryption algorithm "%s"
969            ''',
970            encryption_algo
971        ))
972
973    @property
974    def encryption_cipher(self):
975        """
976        Returns the name of the symmetric encryption cipher to use. The key
977        length can be retrieved via the .key_length property to disabiguate
978        between different variations of TripleDES, AES, and the RC* ciphers.
979
980        :return:
981            A unicode string from one of the following: "rc2", "rc5", "des",
982            "tripledes", "aes"
983        """
984
985        encryption_algo = self['algorithm'].native
986
987        if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
988            return 'aes'
989
990        if encryption_algo in set(['des', 'rc2', 'rc5']):
991            return encryption_algo
992
993        if encryption_algo == 'tripledes_3key':
994            return 'tripledes'
995
996        if encryption_algo == 'pbes2':
997            return self['parameters']['encryption_scheme'].encryption_cipher
998
999        if encryption_algo.find('.') == -1:
1000            return {
1001                'pbes1_md2_des': 'des',
1002                'pbes1_md5_des': 'des',
1003                'pbes1_md2_rc2': 'rc2',
1004                'pbes1_md5_rc2': 'rc2',
1005                'pbes1_sha1_des': 'des',
1006                'pbes1_sha1_rc2': 'rc2',
1007                'pkcs12_sha1_rc4_128': 'rc4',
1008                'pkcs12_sha1_rc4_40': 'rc4',
1009                'pkcs12_sha1_tripledes_3key': 'tripledes',
1010                'pkcs12_sha1_tripledes_2key': 'tripledes',
1011                'pkcs12_sha1_rc2_128': 'rc2',
1012                'pkcs12_sha1_rc2_40': 'rc2',
1013            }[encryption_algo]
1014
1015        raise ValueError(unwrap(
1016            '''
1017            Unrecognized encryption algorithm "%s"
1018            ''',
1019            encryption_algo
1020        ))
1021
1022    @property
1023    def encryption_block_size(self):
1024        """
1025        Returns the block size of the encryption cipher, in bytes.
1026
1027        :return:
1028            An integer that is the block size in bytes
1029        """
1030
1031        encryption_algo = self['algorithm'].native
1032
1033        if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
1034            return 16
1035
1036        cipher_map = {
1037            'des': 8,
1038            'tripledes_3key': 8,
1039            'rc2': 8,
1040        }
1041        if encryption_algo in cipher_map:
1042            return cipher_map[encryption_algo]
1043
1044        if encryption_algo == 'rc5':
1045            return self['parameters'].parsed['block_size_in_bits'].native / 8
1046
1047        if encryption_algo == 'pbes2':
1048            return self['parameters']['encryption_scheme'].encryption_block_size
1049
1050        if encryption_algo.find('.') == -1:
1051            return {
1052                'pbes1_md2_des': 8,
1053                'pbes1_md5_des': 8,
1054                'pbes1_md2_rc2': 8,
1055                'pbes1_md5_rc2': 8,
1056                'pbes1_sha1_des': 8,
1057                'pbes1_sha1_rc2': 8,
1058                'pkcs12_sha1_rc4_128': 0,
1059                'pkcs12_sha1_rc4_40': 0,
1060                'pkcs12_sha1_tripledes_3key': 8,
1061                'pkcs12_sha1_tripledes_2key': 8,
1062                'pkcs12_sha1_rc2_128': 8,
1063                'pkcs12_sha1_rc2_40': 8,
1064            }[encryption_algo]
1065
1066        raise ValueError(unwrap(
1067            '''
1068            Unrecognized encryption algorithm "%s"
1069            ''',
1070            encryption_algo
1071        ))
1072
1073    @property
1074    def encryption_iv(self):
1075        """
1076        Returns the byte string of the initialization vector for the encryption
1077        scheme. Only the PBES2 stores the IV in the params. For PBES1, the IV
1078        is derived from the KDF and this property will return None.
1079
1080        :return:
1081            A byte string or None
1082        """
1083
1084        encryption_algo = self['algorithm'].native
1085
1086        if encryption_algo in set(['rc2', 'rc5']):
1087            return self['parameters'].parsed['iv'].native
1088
1089        # For DES/Triple DES and AES the IV is the entirety of the parameters
1090        octet_string_iv_oids = set([
1091            'des',
1092            'tripledes_3key',
1093            'aes128_cbc',
1094            'aes192_cbc',
1095            'aes256_cbc',
1096            'aes128_ofb',
1097            'aes192_ofb',
1098            'aes256_ofb',
1099        ])
1100        if encryption_algo in octet_string_iv_oids:
1101            return self['parameters'].native
1102
1103        if encryption_algo == 'pbes2':
1104            return self['parameters']['encryption_scheme'].encryption_iv
1105
1106        # All of the PBES1 algos use their KDF to create the IV. For the pbkdf1,
1107        # the KDF is told to generate a key that is an extra 8 bytes long, and
1108        # that is used for the IV. For the PKCS#12 KDF, it is called with an id
1109        # of 2 to generate the IV. In either case, we can't return the IV
1110        # without knowing the user's password.
1111        if encryption_algo.find('.') == -1:
1112            return None
1113
1114        raise ValueError(unwrap(
1115            '''
1116            Unrecognized encryption algorithm "%s"
1117            ''',
1118            encryption_algo
1119        ))
1120
1121
1122class Pbes2Params(Sequence):
1123    _fields = [
1124        ('key_derivation_func', KdfAlgorithm),
1125        ('encryption_scheme', EncryptionAlgorithm),
1126    ]
1127
1128
1129class Pbmac1Params(Sequence):
1130    _fields = [
1131        ('key_derivation_func', KdfAlgorithm),
1132        ('message_auth_scheme', HmacAlgorithm),
1133    ]
1134
1135
1136class Pkcs5MacId(ObjectIdentifier):
1137    _map = {
1138        '1.2.840.113549.1.5.14': 'pbmac1',
1139    }
1140
1141
1142class Pkcs5MacAlgorithm(Sequence):
1143    _fields = [
1144        ('algorithm', Pkcs5MacId),
1145        ('parameters', Any),
1146    ]
1147
1148    _oid_pair = ('algorithm', 'parameters')
1149    _oid_specs = {
1150        'pbmac1': Pbmac1Params,
1151    }
1152
1153
1154EncryptionAlgorithm._oid_specs['pbes2'] = Pbes2Params
1155
1156
1157class AnyAlgorithmId(ObjectIdentifier):
1158    _map = {}
1159
1160    def _setup(self):
1161        _map = self.__class__._map
1162        for other_cls in (EncryptionAlgorithmId, SignedDigestAlgorithmId, DigestAlgorithmId):
1163            for oid, name in other_cls._map.items():
1164                _map[oid] = name
1165
1166
1167class AnyAlgorithmIdentifier(_ForceNullParameters, Sequence):
1168    _fields = [
1169        ('algorithm', AnyAlgorithmId),
1170        ('parameters', Any, {'optional': True}),
1171    ]
1172
1173    _oid_pair = ('algorithm', 'parameters')
1174    _oid_specs = {}
1175
1176    def _setup(self):
1177        Sequence._setup(self)
1178        specs = self.__class__._oid_specs
1179        for other_cls in (EncryptionAlgorithm, SignedDigestAlgorithm):
1180            for oid, spec in other_cls._oid_specs.items():
1181                specs[oid] = spec
1182