Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/asn1crypto/algos.py: 53%
286 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:25 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:25 +0000
1# coding: utf-8
3"""
4ASN.1 type classes for various algorithms using in various aspects of public
5key cryptography. Exports the following items:
7 - AlgorithmIdentifier()
8 - AnyAlgorithmIdentifier()
9 - DigestAlgorithm()
10 - DigestInfo()
11 - DSASignature()
12 - EncryptionAlgorithm()
13 - HmacAlgorithm()
14 - KdfAlgorithm()
15 - Pkcs5MacAlgorithm()
16 - SignedDigestAlgorithm()
18Other type classes are defined that help compose the types listed above.
19"""
21from __future__ import unicode_literals, division, absolute_import, print_function
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)
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
43class AlgorithmIdentifier(Sequence):
44 _fields = [
45 ('algorithm', ObjectIdentifier),
46 ('parameters', Any, {'optional': True}),
47 ]
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 """
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 ])
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]
80 if self['algorithm'].dotted in self._null_algos:
81 return Null
83 return None
85 _spec_callbacks = {
86 'parameters': _parameters_spec
87 }
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
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 }
124class HmacAlgorithm(Sequence):
125 _fields = [
126 ('algorithm', HmacAlgorithmId),
127 ('parameters', Any, {'optional': True}),
128 ]
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 }
153class DigestAlgorithm(_ForceNullParameters, Sequence):
154 _fields = [
155 ('algorithm', DigestAlgorithmId),
156 ('parameters', Any, {'optional': True}),
157 ]
160# This structure is what is signed with a SignedDigestAlgorithm
161class DigestInfo(Sequence):
162 _fields = [
163 ('digest_algorithm', DigestAlgorithm),
164 ('digest', OctetString),
165 ]
168class MaskGenAlgorithmId(ObjectIdentifier):
169 _map = {
170 '1.2.840.113549.1.1.8': 'mgf1',
171 }
174class MaskGenAlgorithm(Sequence):
175 _fields = [
176 ('algorithm', MaskGenAlgorithmId),
177 ('parameters', Any, {'optional': True}),
178 ]
180 _oid_pair = ('algorithm', 'parameters')
181 _oid_specs = {
182 'mgf1': DigestAlgorithm
183 }
186class TrailerField(Integer):
187 _map = {
188 1: 'trailer_field_bc',
189 }
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 ]
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 }
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 }
297class SignedDigestAlgorithm(_ForceNullParameters, Sequence):
298 _fields = [
299 ('algorithm', SignedDigestAlgorithmId),
300 ('parameters', Any, {'optional': True}),
301 ]
303 _oid_pair = ('algorithm', 'parameters')
304 _oid_specs = {
305 'rsassa_pss': RSASSAPSSParams,
306 }
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 """
316 algorithm = self['algorithm'].native
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]
348 raise ValueError(unwrap(
349 '''
350 Signature algorithm not known for %s
351 ''',
352 algorithm
353 ))
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 """
363 algorithm = self['algorithm'].native
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]
387 if algorithm == 'rsassa_pss':
388 return self['parameters']['hash_algorithm']['algorithm'].native
390 raise ValueError(unwrap(
391 '''
392 Hash algorithm not known for %s
393 ''',
394 algorithm
395 ))
398class Pbkdf2Salt(Choice):
399 _alternatives = [
400 ('specified', OctetString),
401 ('other_source', AlgorithmIdentifier),
402 ]
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 ]
414class KdfAlgorithmId(ObjectIdentifier):
415 _map = {
416 '1.2.840.113549.1.5.12': 'pbkdf2'
417 }
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 }
431class DHParameters(Sequence):
432 """
433 Original Name: DHParameter
434 Source: ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-3.asc section 9
435 """
437 _fields = [
438 ('p', Integer),
439 ('g', Integer),
440 ('private_value_length', Integer, {'optional': True}),
441 ]
444class KeyExchangeAlgorithmId(ObjectIdentifier):
445 _map = {
446 '1.2.840.113549.1.3.1': 'dh',
447 }
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 }
461class Rc2Params(Sequence):
462 _fields = [
463 ('rc2_parameter_version', Integer, {'optional': True}),
464 ('iv', OctetString),
465 ]
468class Rc5ParamVersion(Integer):
469 _map = {
470 16: 'v1-0'
471 }
474class Rc5Params(Sequence):
475 _fields = [
476 ('version', Rc5ParamVersion),
477 ('rounds', Integer),
478 ('block_size_in_bits', Integer),
479 ('iv', OctetString, {'optional': True}),
480 ]
483class Pbes1Params(Sequence):
484 _fields = [
485 ('salt', OctetString),
486 ('iterations', Integer),
487 ]
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 ]
499class PSourceAlgorithmId(ObjectIdentifier):
500 _map = {
501 '1.2.840.113549.1.1.9': 'p_specified',
502 }
505class PSourceAlgorithm(Sequence):
506 _fields = [
507 ('algorithm', PSourceAlgorithmId),
508 ('parameters', Any, {'optional': True}),
509 ]
511 _oid_pair = ('algorithm', 'parameters')
512 _oid_specs = {
513 'p_specified': OctetString
514 }
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 ]
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.
558 Original Name: DSS-Sig-Value
559 Source: https://tools.ietf.org/html/rfc3279#section-2.2.2
560 """
562 _fields = [
563 ('r', Integer),
564 ('s', Integer),
565 ]
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.
573 :param data:
574 A byte string from BCryptSignHash()
576 :return:
577 A DSASignature object
578 """
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})
584 def to_p1363(self):
585 """
586 Dumps a signature to a byte string compatible with Microsoft's
587 BCryptVerifySignature() function.
589 :return:
590 A byte string compatible with BCryptVerifySignature()
591 """
593 r_bytes = int_to_bytes(self['r'].native)
594 s_bytes = int_to_bytes(self['s'].native)
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)
600 return r_bytes + s_bytes
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 }
656class EncryptionAlgorithm(_ForceNullParameters, Sequence):
657 _fields = [
658 ('algorithm', EncryptionAlgorithmId),
659 ('parameters', Any, {'optional': True}),
660 ]
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 }
696 @property
697 def kdf(self):
698 """
699 Returns the name of the key derivation function to use.
701 :return:
702 A unicode from of one of the following: "pbkdf1", "pbkdf2",
703 "pkcs12_kdf"
704 """
706 encryption_algo = self['algorithm'].native
708 if encryption_algo == 'pbes2':
709 return self['parameters']['key_derivation_func']['algorithm'].native
711 if encryption_algo.find('.') == -1:
712 if encryption_algo.find('_') != -1:
713 encryption_algo, _ = encryption_algo.split('_', 1)
715 if encryption_algo == 'pbes1':
716 return 'pbkdf1'
718 if encryption_algo == 'pkcs12':
719 return 'pkcs12_kdf'
721 raise ValueError(unwrap(
722 '''
723 Encryption algorithm "%s" does not have a registered key
724 derivation function
725 ''',
726 encryption_algo
727 ))
729 raise ValueError(unwrap(
730 '''
731 Unrecognized encryption algorithm "%s", can not determine key
732 derivation function
733 ''',
734 encryption_algo
735 ))
737 @property
738 def kdf_hmac(self):
739 """
740 Returns the HMAC algorithm to use with the KDF.
742 :return:
743 A unicode string of one of the following: "md2", "md5", "sha1",
744 "sha224", "sha256", "sha384", "sha512"
745 """
747 encryption_algo = self['algorithm'].native
749 if encryption_algo == 'pbes2':
750 return self['parameters']['key_derivation_func']['parameters']['prf']['algorithm'].native
752 if encryption_algo.find('.') == -1:
753 if encryption_algo.find('_') != -1:
754 _, hmac_algo, _ = encryption_algo.split('_', 2)
755 return hmac_algo
757 raise ValueError(unwrap(
758 '''
759 Encryption algorithm "%s" does not have a registered key
760 derivation function
761 ''',
762 encryption_algo
763 ))
765 raise ValueError(unwrap(
766 '''
767 Unrecognized encryption algorithm "%s", can not determine key
768 derivation hmac algorithm
769 ''',
770 encryption_algo
771 ))
773 @property
774 def kdf_salt(self):
775 """
776 Returns the byte string to use as the salt for the KDF.
778 :return:
779 A byte string
780 """
782 encryption_algo = self['algorithm'].native
784 if encryption_algo == 'pbes2':
785 salt = self['parameters']['key_derivation_func']['parameters']['salt']
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 ))
796 return salt.native
798 if encryption_algo.find('.') == -1:
799 if encryption_algo.find('_') != -1:
800 return self['parameters']['salt'].native
802 raise ValueError(unwrap(
803 '''
804 Encryption algorithm "%s" does not have a registered key
805 derivation function
806 ''',
807 encryption_algo
808 ))
810 raise ValueError(unwrap(
811 '''
812 Unrecognized encryption algorithm "%s", can not determine key
813 derivation salt
814 ''',
815 encryption_algo
816 ))
818 @property
819 def kdf_iterations(self):
820 """
821 Returns the number of iterations that should be run via the KDF.
823 :return:
824 An integer
825 """
827 encryption_algo = self['algorithm'].native
829 if encryption_algo == 'pbes2':
830 return self['parameters']['key_derivation_func']['parameters']['iteration_count'].native
832 if encryption_algo.find('.') == -1:
833 if encryption_algo.find('_') != -1:
834 return self['parameters']['iterations'].native
836 raise ValueError(unwrap(
837 '''
838 Encryption algorithm "%s" does not have a registered key
839 derivation function
840 ''',
841 encryption_algo
842 ))
844 raise ValueError(unwrap(
845 '''
846 Unrecognized encryption algorithm "%s", can not determine key
847 derivation iterations
848 ''',
849 encryption_algo
850 ))
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.
861 :raises:
862 ValueError - when the key length can not be determined
864 :return:
865 An integer representing the length in bytes
866 """
868 encryption_algo = self['algorithm'].native
870 if encryption_algo[0:3] == 'aes':
871 return {
872 'aes128_': 16,
873 'aes192_': 24,
874 'aes256_': 32,
875 }[encryption_algo[0:7]]
877 cipher_lengths = {
878 'des': 8,
879 'tripledes_3key': 24,
880 }
882 if encryption_algo in cipher_lengths:
883 return cipher_lengths[encryption_algo]
885 if encryption_algo == 'rc2':
886 rc2_parameter_version = self['parameters']['rc2_parameter_version'].native
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 }
896 if rc2_parameter_version in encoded_key_bits_map:
897 return encoded_key_bits_map[rc2_parameter_version]
899 if rc2_parameter_version >= 256:
900 return rc2_parameter_version
902 if rc2_parameter_version is None:
903 return 4 # 32-bit default
905 raise ValueError(unwrap(
906 '''
907 Invalid RC2 parameter version found in EncryptionAlgorithm
908 parameters
909 '''
910 ))
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
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.
922 return self['parameters']['encryption_scheme'].key_length
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]
940 raise ValueError(unwrap(
941 '''
942 Unrecognized encryption algorithm "%s"
943 ''',
944 encryption_algo
945 ))
947 @property
948 def encryption_mode(self):
949 """
950 Returns the name of the encryption mode to use.
952 :return:
953 A unicode string from one of the following: "cbc", "ecb", "ofb",
954 "cfb", "wrap", "gcm", "ccm", "wrap_pad"
955 """
957 encryption_algo = self['algorithm'].native
959 if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
960 return encryption_algo[7:]
962 if encryption_algo[0:6] == 'pbes1_':
963 return 'cbc'
965 if encryption_algo[0:7] == 'pkcs12_':
966 return 'cbc'
968 if encryption_algo in set(['des', 'tripledes_3key', 'rc2', 'rc5']):
969 return 'cbc'
971 if encryption_algo == 'pbes2':
972 return self['parameters']['encryption_scheme'].encryption_mode
974 raise ValueError(unwrap(
975 '''
976 Unrecognized encryption algorithm "%s"
977 ''',
978 encryption_algo
979 ))
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.
988 :return:
989 A unicode string from one of the following: "rc2", "rc5", "des",
990 "tripledes", "aes"
991 """
993 encryption_algo = self['algorithm'].native
995 if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
996 return 'aes'
998 if encryption_algo in set(['des', 'rc2', 'rc5']):
999 return encryption_algo
1001 if encryption_algo == 'tripledes_3key':
1002 return 'tripledes'
1004 if encryption_algo == 'pbes2':
1005 return self['parameters']['encryption_scheme'].encryption_cipher
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]
1023 raise ValueError(unwrap(
1024 '''
1025 Unrecognized encryption algorithm "%s"
1026 ''',
1027 encryption_algo
1028 ))
1030 @property
1031 def encryption_block_size(self):
1032 """
1033 Returns the block size of the encryption cipher, in bytes.
1035 :return:
1036 An integer that is the block size in bytes
1037 """
1039 encryption_algo = self['algorithm'].native
1041 if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
1042 return 16
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]
1052 if encryption_algo == 'rc5':
1053 return self['parameters']['block_size_in_bits'].native // 8
1055 if encryption_algo == 'pbes2':
1056 return self['parameters']['encryption_scheme'].encryption_block_size
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]
1074 raise ValueError(unwrap(
1075 '''
1076 Unrecognized encryption algorithm "%s"
1077 ''',
1078 encryption_algo
1079 ))
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.
1088 :return:
1089 A byte string or None
1090 """
1092 encryption_algo = self['algorithm'].native
1094 if encryption_algo in set(['rc2', 'rc5']):
1095 return self['parameters']['iv'].native
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
1111 if encryption_algo == 'pbes2':
1112 return self['parameters']['encryption_scheme'].encryption_iv
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
1122 raise ValueError(unwrap(
1123 '''
1124 Unrecognized encryption algorithm "%s"
1125 ''',
1126 encryption_algo
1127 ))
1130class Pbes2Params(Sequence):
1131 _fields = [
1132 ('key_derivation_func', KdfAlgorithm),
1133 ('encryption_scheme', EncryptionAlgorithm),
1134 ]
1137class Pbmac1Params(Sequence):
1138 _fields = [
1139 ('key_derivation_func', KdfAlgorithm),
1140 ('message_auth_scheme', HmacAlgorithm),
1141 ]
1144class Pkcs5MacId(ObjectIdentifier):
1145 _map = {
1146 '1.2.840.113549.1.5.14': 'pbmac1',
1147 }
1150class Pkcs5MacAlgorithm(Sequence):
1151 _fields = [
1152 ('algorithm', Pkcs5MacId),
1153 ('parameters', Any),
1154 ]
1156 _oid_pair = ('algorithm', 'parameters')
1157 _oid_specs = {
1158 'pbmac1': Pbmac1Params,
1159 }
1162EncryptionAlgorithm._oid_specs['pbes2'] = Pbes2Params
1165class AnyAlgorithmId(ObjectIdentifier):
1166 _map = {}
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
1175class AnyAlgorithmIdentifier(_ForceNullParameters, Sequence):
1176 _fields = [
1177 ('algorithm', AnyAlgorithmId),
1178 ('parameters', Any, {'optional': True}),
1179 ]
1181 _oid_pair = ('algorithm', 'parameters')
1182 _oid_specs = {}
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