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