blob: 07b9576e253a225a21db295b6ba9d2a04e454251 [file] [log] [blame]
wbonde91513e2015-06-03 14:52:18 -04001# coding: utf-8
wbondea25fc22015-06-19 15:07:04 -04002
3"""
4ASN.1 type classes for public and private keys. Exports the following items:
5
6 - DSAPrivateKey()
7 - ECPrivateKey()
8 - EncryptedPrivateKeyInfo()
9 - PrivateKeyInfo()
10 - PublicKeyInfo()
11 - RSAPrivateKey()
12 - RSAPublicKey()
13
14Other type classes are defined that help compose the types listed above.
15"""
16
wbond6b66ab52015-06-21 10:26:45 -040017from __future__ import unicode_literals, division, absolute_import, print_function
wbonde91513e2015-06-03 14:52:18 -040018
19import hashlib
wbondf937f482015-06-19 01:51:11 -040020import math
wbonde91513e2015-06-03 14:52:18 -040021
wbond59af99d2015-06-15 16:19:04 -040022from .algos import DigestAlgorithm, EncryptionAlgorithm
wbonde91513e2015-06-03 14:52:18 -040023from .core import (
24 Any,
wbond8b59e052015-06-16 00:13:05 -040025 Asn1Value,
wbonde91513e2015-06-03 14:52:18 -040026 Choice,
27 Integer,
wbonde91513e2015-06-03 14:52:18 -040028 IntegerOctetString,
29 Null,
30 ObjectIdentifier,
31 OctetBitString,
32 OctetString,
33 Sequence,
34 SequenceOf,
35 SetOf,
36)
wbond488eac32015-06-17 23:35:05 -040037from ._elliptic_curve import (
wbonde7c390c2015-06-19 02:01:55 -040038 SECP192R1_BASE_POINT,
39 SECP224R1_BASE_POINT,
40 SECP256R1_BASE_POINT,
41 SECP384R1_BASE_POINT,
42 SECP521R1_BASE_POINT,
wbond488eac32015-06-17 23:35:05 -040043 PrimeCurve,
44 PrimePoint,
45)
wbondc9fec242015-07-13 09:03:11 -040046from ._int import int_from_bytes
wbonde91513e2015-06-03 14:52:18 -040047
48try:
49 # Python 2
50 str_cls = unicode #pylint: disable=E0602
51 byte_cls = str
wbonde91513e2015-06-03 14:52:18 -040052
53except NameError:
54 # Python 3
55 str_cls = str
56 byte_cls = bytes
wbonde91513e2015-06-03 14:52:18 -040057
58
59
60class OtherPrimeInfo(Sequence):
61 """
62 Source: https://tools.ietf.org/html/rfc3447#page-46
63 """
64
65 _fields = [
66 ('prime', Integer),
67 ('exponent', Integer),
68 ('coefficient', Integer),
69 ]
70
71
72class OtherPrimeInfos(SequenceOf):
73 """
74 Source: https://tools.ietf.org/html/rfc3447#page-46
75 """
76
77 _child_spec = OtherPrimeInfo
78
79
80class RSAPrivateKeyVersion(Integer):
81 """
82 Original Name: Version
83 Source: https://tools.ietf.org/html/rfc3447#page-45
84 """
85
86 _map = {
87 0: 'two-prime',
88 1: 'multi',
89 }
90
91
92class RSAPrivateKey(Sequence):
93 """
94 Source: https://tools.ietf.org/html/rfc3447#page-45
95 """
96
97 _fields = [
98 ('version', RSAPrivateKeyVersion),
99 ('modulus', Integer),
100 ('public_exponent', Integer),
101 ('private_exponent', Integer),
102 ('prime1', Integer),
103 ('prime2', Integer),
104 ('exponent1', Integer),
105 ('exponent2', Integer),
106 ('coefficient', Integer),
107 ('other_prime_infos', OtherPrimeInfos, {'optional': True})
108 ]
109
110
111class RSAPublicKey(Sequence):
112 """
113 Source: https://tools.ietf.org/html/rfc3447#page-44
114 """
115
116 _fields = [
117 ('modulus', Integer),
118 ('public_exponent', Integer)
119 ]
120
121
122class DSAPrivateKey(Sequence):
123 """
124 The ASN1 structure that OpenSSL uses to store a DSA private key that is
125 not part of a PKCS#8 structure. Reversed engineered from english-language
126 description on linked OpenSSL documentation page.
127
128 Original Name: None
129 Source: https://www.openssl.org/docs/apps/dsa.html
130 """
131
132 _fields = [
133 ('version', Integer),
134 ('p', Integer),
135 ('q', Integer),
136 ('g', Integer),
137 ('public_key', Integer),
138 ('private_key', Integer),
139 ]
140
141
142class SpecifiedECDomainVersion(Integer):
143 """
144 Source: http://www.secg.org/sec1-v2.pdf page 104
145 """
146 _map = {
147 1: 'ecdpVer1',
148 2: 'ecdpVer2',
149 3: 'ecdpVer3',
150 }
151
152
153class FieldType(ObjectIdentifier):
154 """
155 Original Name: None
156 Source: http://www.secg.org/sec1-v2.pdf page 101
157 """
158
159 _map = {
160 '1.2.840.10045.1.1': 'prime_field',
161 '1.2.840.10045.1.2': 'characteristic_two_field',
162 }
163
164
165class CharacteristicTwoBasis(ObjectIdentifier):
166 """
167 Original Name: None
168 Source: http://www.secg.org/sec1-v2.pdf page 102
169 """
170
171 _map = {
172 '1.2.840.10045.1.2.1.1': 'gn_basis',
173 '1.2.840.10045.1.2.1.2': 'tp_basis',
174 '1.2.840.10045.1.2.1.3': 'pp_basis',
175 }
176
177
178class Pentanomial(Sequence):
179 """
180 Source: http://www.secg.org/sec1-v2.pdf page 102
181 """
182
183 _fields = [
184 ('k1', Integer),
185 ('k2', Integer),
186 ('k3', Integer),
187 ]
188
189
190class CharacteristicTwo(Sequence):
191 """
192 Original Name: Characteristic-two
193 Source: http://www.secg.org/sec1-v2.pdf page 101
194 """
195
196 _fields = [
197 ('m', Integer),
198 ('basis', CharacteristicTwoBasis),
199 ('parameters', Any),
200 ]
201
202 _oid_pair = ('basis', 'parameters')
203 _oid_specs = {
204 'gn_basis': Null,
205 'tp_basis': Integer,
206 'pp_basis': Pentanomial,
207 }
208
209
210class FieldID(Sequence):
211 """
212 Source: http://www.secg.org/sec1-v2.pdf page 100
213 """
214
215 _fields = [
216 ('field_type', FieldType),
217 ('parameters', Any),
218 ]
219
220 _oid_pair = ('field_type', 'parameters')
221 _oid_specs = {
222 'prime_field': Integer,
223 'characteristic_two_field': CharacteristicTwo,
224 }
225
226
227class Curve(Sequence):
228 """
229 Source: http://www.secg.org/sec1-v2.pdf page 104
230 """
231
232 _fields = [
233 ('a', OctetString),
234 ('b', OctetString),
235 ('seed', OctetBitString, {'optional': True}),
236 ]
237
238
239class SpecifiedECDomain(Sequence):
240 """
241 Source: http://www.secg.org/sec1-v2.pdf page 103
242 """
243
244 _fields = [
245 ('version', SpecifiedECDomainVersion),
246 ('field_id', FieldID),
247 ('curve', Curve),
248 ('base', OctetString),
249 ('order', Integer),
250 ('cofactor', Integer, {'optional': True}),
251 ('hash', DigestAlgorithm, {'optional': True}),
252 ]
253
254
255class NamedCurve(ObjectIdentifier):
256 """
257 Various named curves
258
259 Original Name: None
260 Source: https://tools.ietf.org/html/rfc3279#page-23,
261 https://tools.ietf.org/html/rfc5480#page-5
262 """
263
264 _map = {
265 # https://tools.ietf.org/html/rfc3279#page-23
266 '1.2.840.10045.3.0.1': 'c2pnb163v1',
267 '1.2.840.10045.3.0.2': 'c2pnb163v2',
268 '1.2.840.10045.3.0.3': 'c2pnb163v3',
269 '1.2.840.10045.3.0.4': 'c2pnb176w1',
270 '1.2.840.10045.3.0.5': 'c2tnb191v1',
271 '1.2.840.10045.3.0.6': 'c2tnb191v2',
272 '1.2.840.10045.3.0.7': 'c2tnb191v3',
273 '1.2.840.10045.3.0.8': 'c2onb191v4',
274 '1.2.840.10045.3.0.9': 'c2onb191v5',
275 '1.2.840.10045.3.0.10': 'c2pnb208w1',
276 '1.2.840.10045.3.0.11': 'c2tnb239v1',
277 '1.2.840.10045.3.0.12': 'c2tnb239v2',
278 '1.2.840.10045.3.0.13': 'c2tnb239v3',
279 '1.2.840.10045.3.0.14': 'c2onb239v4',
280 '1.2.840.10045.3.0.15': 'c2onb239v5',
281 '1.2.840.10045.3.0.16': 'c2pnb272w1',
282 '1.2.840.10045.3.0.17': 'c2pnb304w1',
283 '1.2.840.10045.3.0.18': 'c2tnb359v1',
284 '1.2.840.10045.3.0.19': 'c2pnb368w1',
285 '1.2.840.10045.3.0.20': 'c2tnb431r1',
wbonde91513e2015-06-03 14:52:18 -0400286 '1.2.840.10045.3.1.2': 'prime192v2',
287 '1.2.840.10045.3.1.3': 'prime192v3',
288 '1.2.840.10045.3.1.4': 'prime239v1',
289 '1.2.840.10045.3.1.5': 'prime239v2',
290 '1.2.840.10045.3.1.6': 'prime239v3',
wbonde91513e2015-06-03 14:52:18 -0400291 # https://tools.ietf.org/html/rfc5480#page-5
292 '1.3.132.0.1': 'sect163k1',
293 '1.3.132.0.15': 'sect163r2',
wbondf937f482015-06-19 01:51:11 -0400294 '1.2.840.10045.3.1.1': 'secp192r1',
wbonde91513e2015-06-03 14:52:18 -0400295 '1.3.132.0.33': 'secp224r1',
296 '1.3.132.0.26': 'sect233k1',
wbondf937f482015-06-19 01:51:11 -0400297 '1.2.840.10045.3.1.7': 'secp256r1',
wbonde91513e2015-06-03 14:52:18 -0400298 '1.3.132.0.27': 'sect233r1',
299 '1.3.132.0.16': 'sect283k1',
300 '1.3.132.0.17': 'sect283r1',
301 '1.3.132.0.34': 'secp384r1',
302 '1.3.132.0.36': 'sect409k1',
303 '1.3.132.0.37': 'sect409r1',
304 '1.3.132.0.35': 'secp521r1',
305 '1.3.132.0.38': 'sect571k1',
306 '1.3.132.0.39': 'sect571r1',
307 }
308
309
310class ECDomainParameters(Choice):
311 """
312 Source: http://www.secg.org/sec1-v2.pdf page 102
313 """
314
315 _alternatives = [
316 ('specified', SpecifiedECDomain),
317 ('named', NamedCurve),
318 ('implicit_ca', Null),
319 ]
320
321
322class ECPrivateKeyVersion(Integer):
323 """
324 Original Name: None
325 Source: http://www.secg.org/sec1-v2.pdf page 108
326 """
327
328 _map = {
329 1: 'ecPrivkeyVer1',
330 }
331
332
333class ECPrivateKey(Sequence):
334 """
335 Source: http://www.secg.org/sec1-v2.pdf page 108
336 """
337
338 _fields = [
339 ('version', ECPrivateKeyVersion),
340 ('private_key', IntegerOctetString),
341 ('parameters', ECDomainParameters, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
wbond29f81492015-06-17 22:35:33 -0400342 ('public_key', OctetBitString, {'tag_type': 'explicit', 'tag': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -0400343 ]
344
345
wbonde91513e2015-06-03 14:52:18 -0400346class DSAParams(Sequence):
347 """
348 Parameters for a DSA public or private key
349
350 Original Name: Dss-Parms
351 Source: https://tools.ietf.org/html/rfc3279#page-9
352 """
353
354 _fields = [
355 ('p', Integer),
356 ('q', Integer),
357 ('g', Integer),
358 ]
359
360
361class Attribute(Sequence):
362 """
363 Source: https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.501-198811-S!!PDF-E&type=items page 8
364 """
365
366 _fields = [
367 ('type', ObjectIdentifier),
368 ('values', SetOf, {'spec': Any}),
369 ]
370
371
372class Attributes(SetOf):
373 """
374 Source: https://tools.ietf.org/html/rfc5208#page-3
375 """
376
377 _child_spec = Attribute
378
379
380class PrivateKeyAlgorithmId(ObjectIdentifier):
381 """
382 These OIDs for various public keys are reused when storing private keys
383 inside of a PKCS#8 structure
384
385 Original Name: None
386 Source: https://tools.ietf.org/html/rfc3279
387 """
388
389 _map = {
390 # https://tools.ietf.org/html/rfc3279#page-19
391 '1.2.840.113549.1.1.1': 'rsa',
392 # https://tools.ietf.org/html/rfc3279#page-18
393 '1.2.840.10040.4.1': 'dsa',
394 # https://tools.ietf.org/html/rfc3279#page-13
wbond680cba12015-07-01 23:53:54 -0400395 '1.2.840.10045.2.1': 'ec',
wbonde91513e2015-06-03 14:52:18 -0400396 }
397
398
399class PrivateKeyAlgorithm(Sequence):
400 """
401 Original Name: PrivateKeyAlgorithmIdentifier
402 Source: https://tools.ietf.org/html/rfc5208#page-3
403 """
404
405 _fields = [
406 ('algorithm', PrivateKeyAlgorithmId),
407 ('parameters', Any, {'optional': True}),
408 ]
409
410 _oid_pair = ('algorithm', 'parameters')
411 _oid_specs = {
412 'rsa': Null,
413 'dsa': DSAParams,
wbond680cba12015-07-01 23:53:54 -0400414 'ec': ECDomainParameters,
wbonde91513e2015-06-03 14:52:18 -0400415 }
416
417
418class PrivateKeyInfo(Sequence):
419 """
420 Source: https://tools.ietf.org/html/rfc5208#page-3
421 """
422
423 _fields = [
424 ('version', Integer),
425 ('private_key_algorithm', PrivateKeyAlgorithm),
426 ('private_key', OctetString),
427 ('attributes', Attributes, {'tag_type': 'implicit', 'tag': 0, 'optional': True}),
428 ]
429
430 def _private_key_spec(self):
431 algorithm = self['private_key_algorithm']['algorithm'].native
432 return {
433 'rsa': RSAPrivateKey,
434 'dsa': Integer,
wbond680cba12015-07-01 23:53:54 -0400435 'ec': ECPrivateKey,
wbonde91513e2015-06-03 14:52:18 -0400436 }[algorithm]
437
438 _spec_callbacks = {
439 'private_key': _private_key_spec
440 }
441
wbondf937f482015-06-19 01:51:11 -0400442 _algorithm = None
443 _bit_size = None
wbondeb5eb6e2015-06-18 14:14:12 -0400444 _public_key = None
wbonde91513e2015-06-03 14:52:18 -0400445 _fingerprint = None
446
wbond8b59e052015-06-16 00:13:05 -0400447 @classmethod
448 def wrap(cls, private_key, algorithm):
449 """
450 Wraps a private key in a PrivateKeyInfo structure
451
452 :param private_key:
453 A byte string or Asn1Value object of the private key
454
455 :param algorithm:
wbond680cba12015-07-01 23:53:54 -0400456 A unicode string of "rsa", "dsa" or "ec"
wbond8b59e052015-06-16 00:13:05 -0400457
458 :return:
459 A PrivateKeyInfo object
460 """
461
462 if not isinstance(private_key, byte_cls) and not isinstance(private_key, Asn1Value):
463 raise ValueError('private_key must be a byte string or Asn1Value, not %s' % private_key.__class__.__name__)
464
465 if algorithm == 'rsa':
466 if not isinstance(private_key, RSAPrivateKey):
467 private_key = RSAPrivateKey.load(private_key)
468 params = Null()
469 elif algorithm == 'dsa':
470 if not isinstance(private_key, DSAPrivateKey):
471 private_key = DSAPrivateKey.load(private_key)
472 params = DSAParams()
473 params['p'] = private_key['p']
474 params['q'] = private_key['q']
475 params['g'] = private_key['g']
wbondeb5eb6e2015-06-18 14:14:12 -0400476 public_key = private_key['public_key']
wbonda4bfddd2015-06-17 23:36:16 -0400477 private_key = private_key['private_key']
wbond680cba12015-07-01 23:53:54 -0400478 elif algorithm == 'ec':
wbond8b59e052015-06-16 00:13:05 -0400479 if not isinstance(private_key, ECPrivateKey):
480 private_key = ECPrivateKey.load(private_key)
481 params = private_key['parameters']
wbonda4bfddd2015-06-17 23:36:16 -0400482 del private_key['parameters']
wbond8b59e052015-06-16 00:13:05 -0400483 else:
wbond680cba12015-07-01 23:53:54 -0400484 raise ValueError('algorithm must be one of "rsa", "dsa", "ec" - is %s' % repr(algorithm))
wbond8b59e052015-06-16 00:13:05 -0400485
486 private_key_algo = PrivateKeyAlgorithm()
487 private_key_algo['algorithm'] = PrivateKeyAlgorithmId(algorithm)
488 private_key_algo['parameters'] = params
489
490 container = cls()
wbondf937f482015-06-19 01:51:11 -0400491 container._algorithm = algorithm #pylint: disable=W0212
wbond8b59e052015-06-16 00:13:05 -0400492 container['version'] = Integer(0)
493 container['private_key_algorithm'] = private_key_algo
wbonde95da2c2015-06-19 01:49:44 -0400494 container['private_key'] = OctetString(private_key.untag().dump())
wbond8b59e052015-06-16 00:13:05 -0400495
wbondeb5eb6e2015-06-18 14:14:12 -0400496 # Here we save the DSA public key if possible since it is not contained
497 # within the PKCS#8 structure for a DSA key
498 if algorithm == 'dsa':
499 container._public_key = public_key #pylint: disable=W0212
500
wbond8b59e052015-06-16 00:13:05 -0400501 return container
502
wbondeb5eb6e2015-06-18 14:14:12 -0400503 def _compute_public_key(self):
wbond488eac32015-06-17 23:35:05 -0400504 """
505 Computes the public key corresponding to the current private key.
506
507 :return:
508 For RSA keys, an RSAPublicKey object. For DSA keys, an Integer
wbond680cba12015-07-01 23:53:54 -0400509 object. For EC keys, an OctetString.
wbond488eac32015-06-17 23:35:05 -0400510 """
511
wbondf937f482015-06-19 01:51:11 -0400512 if self.algorithm == 'dsa':
wbondeb5eb6e2015-06-18 14:14:12 -0400513 params = self['private_key_algorithm']['parameters']
514 return Integer(pow(
515 params['g'].native,
516 self['private_key'].native,
517 params['p'].native
518 ))
wbond488eac32015-06-17 23:35:05 -0400519
wbondf937f482015-06-19 01:51:11 -0400520 if self.algorithm == 'rsa':
wbondeb5eb6e2015-06-18 14:14:12 -0400521 key = self['private_key'].parsed
522 return RSAPublicKey({
523 'modulus': key['modulus'],
524 'public_exponent': key['public_exponent'],
525 })
526
wbond680cba12015-07-01 23:53:54 -0400527 if self.algorithm == 'ec':
wbondf937f482015-06-19 01:51:11 -0400528 curve_type, details = self.curve
wbondeb5eb6e2015-06-18 14:14:12 -0400529
wbondf937f482015-06-19 01:51:11 -0400530 if curve_type == 'implicit_ca':
wbond680cba12015-07-01 23:53:54 -0400531 raise ValueError('Unable to compute public key for EC key using Implicit CA parameters')
wbondeb5eb6e2015-06-18 14:14:12 -0400532
wbondf937f482015-06-19 01:51:11 -0400533 if curve_type == 'specified':
534 if details['field_id']['field_type'] == 'characteristic_two_field':
wbond680cba12015-07-01 23:53:54 -0400535 raise ValueError('Unable to compute public key for EC key over a characteristic two field')
wbondeb5eb6e2015-06-18 14:14:12 -0400536
537 curve = PrimeCurve(
wbondf937f482015-06-19 01:51:11 -0400538 details['field_id']['parameters'],
539 int_from_bytes(details['curve']['a']),
540 int_from_bytes(details['curve']['b'])
wbondeb5eb6e2015-06-18 14:14:12 -0400541 )
wbondf937f482015-06-19 01:51:11 -0400542 base_point = PrimePoint.load(curve, details['base'])
wbondeb5eb6e2015-06-18 14:14:12 -0400543
wbondf937f482015-06-19 01:51:11 -0400544 elif curve_type == 'named':
545 if details not in ('secp192r1', 'secp224r1', 'secp256r1', 'secp384r1', 'secp521r1'):
wbond680cba12015-07-01 23:53:54 -0400546 raise ValueError('Unable to compute public key for EC named curve %s, parameters not currently included' % details)
wbondeb5eb6e2015-06-18 14:14:12 -0400547
548 base_point = {
wbonde7c390c2015-06-19 02:01:55 -0400549 'secp192r1': SECP192R1_BASE_POINT,
550 'secp224r1': SECP224R1_BASE_POINT,
551 'secp256r1': SECP256R1_BASE_POINT,
552 'secp384r1': SECP384R1_BASE_POINT,
553 'secp521r1': SECP521R1_BASE_POINT,
wbondf937f482015-06-19 01:51:11 -0400554 }[details]
wbondeb5eb6e2015-06-18 14:14:12 -0400555
556 public_point = base_point * self['private_key'].parsed['private_key'].native
557 return OctetString(public_point.dump())
558
559 def unwrap(self):
560 """
561 Unwraps the private key into an RSAPrivateKey, DSAPrivateKey or
562 ECPrivateKey object
563
564 :return:
565 An RSAPrivateKey, DSAPrivateKey or ECPrivateKey object
566 """
567
wbondf937f482015-06-19 01:51:11 -0400568 if self.algorithm == 'rsa':
569 return self['private_key'].parsed
wbondeb5eb6e2015-06-18 14:14:12 -0400570
wbondf937f482015-06-19 01:51:11 -0400571 if self.algorithm == 'dsa':
wbondeb5eb6e2015-06-18 14:14:12 -0400572 params = self['private_key_algorithm']['parameters']
573 return DSAPrivateKey({
574 'version': 0,
575 'p': params['p'],
576 'q': params['q'],
577 'g': params['g'],
578 'public_key': self.public_key,
wbondf937f482015-06-19 01:51:11 -0400579 'private_key': self['private_key'].parsed,
wbondeb5eb6e2015-06-18 14:14:12 -0400580 })
581
wbond680cba12015-07-01 23:53:54 -0400582 if self.algorithm == 'ec':
wbondf937f482015-06-19 01:51:11 -0400583 output = self['private_key'].parsed
wbondeb5eb6e2015-06-18 14:14:12 -0400584 output['parameters'] = self['private_key_algorithm']['parameters']
585 output['public_key'] = OctetBitString(self.public_key.native)
586 return output
587
588 @property
wbondf937f482015-06-19 01:51:11 -0400589 def curve(self):
590 """
wbond680cba12015-07-01 23:53:54 -0400591 Returns information about the curve used for an EC key
wbondf937f482015-06-19 01:51:11 -0400592
593 :raises:
wbond680cba12015-07-01 23:53:54 -0400594 ValueError - when the key is not an EC key
wbondf937f482015-06-19 01:51:11 -0400595
596 :return:
597 A two-element tuple, with the first element being a unicode string
598 of "implicit_ca", "specified" or "named". If the first element is
599 "implicit_ca", the second is None. If "specified", the second is
600 an OrderedDict that is the native version of SpecifiedECDomain. If
601 "named", the second is a unicode string of the curve name.
602 """
603
wbond680cba12015-07-01 23:53:54 -0400604 if self.algorithm != 'ec':
605 raise ValueError('Only EC keys have a curve, this key is %s' % self.algorithm.upper())
wbondf937f482015-06-19 01:51:11 -0400606
607 params = self['private_key_algorithm']['parameters']
608 chosen = params.chosen
609
610 if params.name == 'implicit_ca':
611 value = None
612 else:
613 value = chosen.native
614
615 return (params.name, value)
616
617 @property
wbond1b00a592015-06-28 12:58:26 -0400618 def hash_algo(self):
619 """
620 Returns the name of the family of hash algorithms used to generate a
621 DSA key
622
623 :raises:
624 ValueError - when the key is not a DSA key
625
626 :return:
627 A unicode string of "sha1" or "sha2"
628 """
629
630 if self.algorithm != 'dsa':
631 raise ValueError('Only DSA keys are generated using a hash algorithm, this key is %s' % self.algorithm.upper())
632
633 byte_len = math.log(self['private_key_algorithm']['parameters']['q'].native, 2) / 8
634
635 return 'sha1' if byte_len <= 20 else 'sha2'
636
637 @property
wbondf937f482015-06-19 01:51:11 -0400638 def algorithm(self):
639 """
640 :return:
wbond680cba12015-07-01 23:53:54 -0400641 A unicode string of "rsa", "dsa" or "ec"
wbondf937f482015-06-19 01:51:11 -0400642 """
643
644 if self._algorithm is None:
645 self._algorithm = self['private_key_algorithm']['algorithm'].native
646 return self._algorithm
647
648 @property
649 def bit_size(self):
650 """
651 :return:
wbondf4f176c2015-07-07 23:27:19 -0400652 The bit size of the private key, as an integer
wbondf937f482015-06-19 01:51:11 -0400653 """
654
655 if self._bit_size is None:
656 if self.algorithm == 'rsa':
wbondf4f176c2015-07-07 23:27:19 -0400657 prime = self['private_key'].parsed['modulus'].native
wbondf937f482015-06-19 01:51:11 -0400658 elif self.algorithm == 'dsa':
659 prime = self['private_key_algorithm']['parameters']['p'].native
wbond680cba12015-07-01 23:53:54 -0400660 elif self.algorithm == 'ec':
wbondf937f482015-06-19 01:51:11 -0400661 prime = self['private_key'].parsed['private_key'].native
wbondf4f176c2015-07-07 23:27:19 -0400662 self._bit_size = int(math.ceil(math.log(prime, 2)))
wbondf937f482015-06-19 01:51:11 -0400663 return self._bit_size
664
665 @property
wbondf4f176c2015-07-07 23:27:19 -0400666 def byte_size(self):
667 """
668 :return:
669 The byte size of the private key, as an integer
670 """
671
672 return int(math.ceil(self.bit_size / 8))
673
674 @property
wbondeb5eb6e2015-06-18 14:14:12 -0400675 def public_key(self):
wbondf937f482015-06-19 01:51:11 -0400676 """
677 :return:
678 If an RSA key, an RSAPublicKey object. If a DSA key, an Integer
wbond680cba12015-07-01 23:53:54 -0400679 object. If an EC key, an OctetString object.
wbondf937f482015-06-19 01:51:11 -0400680 """
681
wbondeb5eb6e2015-06-18 14:14:12 -0400682 if self._public_key is None:
wbond680cba12015-07-01 23:53:54 -0400683 if self.algorithm == 'ec':
wbond488eac32015-06-17 23:35:05 -0400684 key = self['private_key'].parsed
wbondeb5eb6e2015-06-18 14:14:12 -0400685 if key['public_key']:
686 self._public_key = OctetString(key['public_key'].native)
687 else:
688 self._public_key = self._compute_public_key()
689 else:
690 self._public_key = self._compute_public_key()
wbond488eac32015-06-17 23:35:05 -0400691
wbondeb5eb6e2015-06-18 14:14:12 -0400692 return self._public_key
wbond488eac32015-06-17 23:35:05 -0400693
wbonde91513e2015-06-03 14:52:18 -0400694 @property
695 def fingerprint(self):
696 """
697 Creates a fingerprint that can be compared with a public key to see if
698 the two form a pair.
699
700 This fingerprint is not compatiable with fingerprints generated by any
701 other software.
702
wbonde91513e2015-06-03 14:52:18 -0400703 :return:
704 A byte string that is a sha256 hash of selected components (based
705 on the key type)
706 """
707
708 if self._fingerprint is None:
wbondeb02d762015-06-17 23:37:30 -0400709 params = self['private_key_algorithm']['parameters']
wbonde91513e2015-06-03 14:52:18 -0400710 key = self['private_key'].parsed
711
wbondf937f482015-06-19 01:51:11 -0400712 if self.algorithm == 'rsa':
wbonde91513e2015-06-03 14:52:18 -0400713 to_hash = '%d:%d' % (
714 key['modulus'].native,
715 key['public_exponent'].native,
716 )
717
wbondf937f482015-06-19 01:51:11 -0400718 elif self.algorithm == 'dsa':
wbondeb5eb6e2015-06-18 14:14:12 -0400719 public_key = self.public_key
wbonde91513e2015-06-03 14:52:18 -0400720 to_hash = '%d:%d:%d:%d' % (
721 params['p'].native,
722 params['q'].native,
723 params['g'].native,
wbondeb02d762015-06-17 23:37:30 -0400724 public_key.native,
wbonde91513e2015-06-03 14:52:18 -0400725 )
726
wbond680cba12015-07-01 23:53:54 -0400727 elif self.algorithm == 'ec':
wbonde91513e2015-06-03 14:52:18 -0400728 public_key = key['public_key'].native
729 if public_key is None:
wbondeb5eb6e2015-06-18 14:14:12 -0400730 public_key = self.public_key.native
wbonde91513e2015-06-03 14:52:18 -0400731
732 if params.name == 'named':
wbondeb02d762015-06-17 23:37:30 -0400733 to_hash = '%s:' % params.chosen.native
734 to_hash = to_hash.encode('utf-8')
735 to_hash += public_key
wbonde91513e2015-06-03 14:52:18 -0400736
wbondeb02d762015-06-17 23:37:30 -0400737 elif params.name == 'implicit_ca':
738 to_hash = public_key
wbonde91513e2015-06-03 14:52:18 -0400739
wbondeb02d762015-06-17 23:37:30 -0400740 elif params.name == 'specified':
741 to_hash = '%s:' % params.chosen['field_id']['parameters'].native
742 to_hash = to_hash.encode('utf-8')
743 to_hash += b':' + params.chosen['curve']['a'].native
744 to_hash += b':' + params.chosen['curve']['b'].native
745 to_hash += public_key
wbonde91513e2015-06-03 14:52:18 -0400746
747 if isinstance(to_hash, str_cls):
748 to_hash = to_hash.encode('utf-8')
749
750 self._fingerprint = hashlib.sha256(to_hash).digest()
751
752 return self._fingerprint
753
754
755class EncryptedPrivateKeyInfo(Sequence):
756 """
757 Source: https://tools.ietf.org/html/rfc5208#page-4
758 """
759
760 _fields = [
wbond59af99d2015-06-15 16:19:04 -0400761 ('encryption_algorithm', EncryptionAlgorithm),
wbonde91513e2015-06-03 14:52:18 -0400762 ('encrypted_data', OctetString),
763 ]
764
765
766# These structures are from https://tools.ietf.org/html/rfc3279
767
768class PublicKeyAlgorithmId(ObjectIdentifier):
769 """
770 Original Name: None
771 Source: https://tools.ietf.org/html/rfc3279
772 """
773
774 _map = {
775 # https://tools.ietf.org/html/rfc3279#page-19
776 '1.2.840.113549.1.1.1': 'rsa',
777 # https://tools.ietf.org/html/rfc3279#page-18
778 '1.2.840.10040.4.1': 'dsa',
779 # https://tools.ietf.org/html/rfc3279#page-13
wbond680cba12015-07-01 23:53:54 -0400780 '1.2.840.10045.2.1': 'ec',
wbonde91513e2015-06-03 14:52:18 -0400781 }
782
783
784class PublicKeyAlgorithm(Sequence):
785 """
786 Original Name: AlgorithmIdentifier
787 Source: https://tools.ietf.org/html/rfc5280#page-18
788 """
789
790 _fields = [
791 ('algorithm', PublicKeyAlgorithmId),
792 ('parameters', Any, {'optional': True}),
793 ]
794
795 _oid_pair = ('algorithm', 'parameters')
796 _oid_specs = {
797 'rsa': Null,
798 'dsa': DSAParams,
wbond680cba12015-07-01 23:53:54 -0400799 'ec': ECDomainParameters,
wbonde91513e2015-06-03 14:52:18 -0400800 }
801
802
803class PublicKeyInfo(Sequence):
804 """
805 Original Name: SubjectPublicKeyInfo
806 Source: https://tools.ietf.org/html/rfc5280#page-17
807 """
808
809 _fields = [
810 ('algorithm', PublicKeyAlgorithm),
811 ('public_key', OctetBitString),
812 ]
813
814 def _public_key_spec(self):
815 algorithm = self['algorithm']['algorithm'].native
816 return {
817 'rsa': RSAPublicKey,
818 'dsa': Integer,
819 # ECSDA's public key is an ECPoint, which is an OctetString. Since
820 # we are using OctetBitString here, we don't need further parsing.
wbond680cba12015-07-01 23:53:54 -0400821 'ec': None,
wbonde91513e2015-06-03 14:52:18 -0400822 }[algorithm]
823
824 _spec_callbacks = {
825 'public_key': _public_key_spec
826 }
827
wbondf937f482015-06-19 01:51:11 -0400828 _algorithm = None
829 _bit_size = None
wbonde91513e2015-06-03 14:52:18 -0400830 _fingerprint = None
831
wbond8b59e052015-06-16 00:13:05 -0400832 @classmethod
833 def wrap(cls, public_key, algorithm):
834 """
835 Wraps a public key in a PublicKeyInfo structure
836
837 :param public_key:
838 A byte string or Asn1Value object of the public key
839
840 :param algorithm:
841 A unicode string of "rsa"
842
843 :return:
844 A PublicKeyInfo object
845 """
846
847 if not isinstance(public_key, byte_cls) and not isinstance(public_key, Asn1Value):
848 raise ValueError('public_key must be a byte string or Asn1Value, not %s' % public_key.__class__.__name__)
849
850 if algorithm != 'rsa':
851 raise ValueError('algorithm must be one of "rsa" - is %s' % repr(algorithm))
852
853 algo = PublicKeyAlgorithm()
854 algo['algorithm'] = PublicKeyAlgorithmId(algorithm)
855 algo['parameters'] = Null()
856
857 container = cls()
858 container['algorithm'] = algo
859 if isinstance(public_key, Asn1Value):
wbonde95da2c2015-06-19 01:49:44 -0400860 public_key = public_key.untag().dump()
wbond8b59e052015-06-16 00:13:05 -0400861 container['public_key'] = OctetBitString(public_key)
862
863 return container
864
wbondb924d332015-07-07 23:27:48 -0400865 def unwrap(self):
866 """
867 Unwraps an RSA public key into an RSAPublicKey object. Does not support
868 DSA or EC public keys since they do not have an unwrapped form.
869
870 :return:
871 An RSAPublicKey object
872 """
873
874 if self.algorithm == 'rsa':
875 return self['public_key'].parsed
876
877 key_type = self.algorithm.upper()
878 a_an = 'an' if key_type == 'EC' else 'a'
879 raise ValueError('Only RSA public keys may be unwrapped - this key is %s %s public key' % (a_an, key_type))
880
wbonde91513e2015-06-03 14:52:18 -0400881 @property
wbondf937f482015-06-19 01:51:11 -0400882 def curve(self):
883 """
wbond680cba12015-07-01 23:53:54 -0400884 Returns information about the curve used for an EC key
wbondf937f482015-06-19 01:51:11 -0400885
886 :raises:
wbond680cba12015-07-01 23:53:54 -0400887 ValueError - when the key is not an EC key
wbondf937f482015-06-19 01:51:11 -0400888
889 :return:
890 A two-element tuple, with the first element being a unicode string
891 of "implicit_ca", "specified" or "named". If the first element is
892 "implicit_ca", the second is None. If "specified", the second is
893 an OrderedDict that is the native version of SpecifiedECDomain. If
894 "named", the second is a unicode string of the curve name.
895 """
896
wbond680cba12015-07-01 23:53:54 -0400897 if self.algorithm != 'ec':
898 raise ValueError('Only EC keys have a curve, this key is %s' % self.algorithm.upper())
wbondf937f482015-06-19 01:51:11 -0400899
900 params = self['algorithm']['parameters']
901 chosen = params.chosen
902
903 if params.name == 'implicit_ca':
904 value = None
905 else:
906 value = chosen.native
907
908 return (params.name, value)
909
910 @property
wbond1b00a592015-06-28 12:58:26 -0400911 def hash_algo(self):
912 """
913 Returns the name of the family of hash algorithms used to generate a
914 DSA key
915
916 :raises:
917 ValueError - when the key is not a DSA key
918
919 :return:
920 A unicode string of "sha1" or "sha2"
921 """
922
923 if self.algorithm != 'dsa':
924 raise ValueError('Only DSA keys are generated using a hash algorithm, this key is %s' % self.algorithm.upper())
925
926 byte_len = math.log(self['algorithm']['parameters']['q'].native, 2) / 8
927
928 return 'sha1' if byte_len <= 20 else 'sha2'
929
930 @property
wbondf937f482015-06-19 01:51:11 -0400931 def algorithm(self):
932 """
933 :return:
wbond680cba12015-07-01 23:53:54 -0400934 A unicode string of "rsa", "dsa" or "ec"
wbondf937f482015-06-19 01:51:11 -0400935 """
936
937 if self._algorithm is None:
938 self._algorithm = self['algorithm']['algorithm'].native
939 return self._algorithm
940
941 @property
942 def bit_size(self):
943 """
944 :return:
wbondf4f176c2015-07-07 23:27:19 -0400945 The bit size of the public key, as an integer
wbondf937f482015-06-19 01:51:11 -0400946 """
947
948 if self._bit_size is None:
wbond680cba12015-07-01 23:53:54 -0400949 if self.algorithm == 'ec':
wbondf937f482015-06-19 01:51:11 -0400950 self._bit_size = ((len(self['public_key'].native) - 1) / 2) * 8
951 else:
952 if self.algorithm == 'rsa':
953 prime = self['public_key'].parsed['modulus'].native
954 elif self.algorithm == 'dsa':
wbondd6b8b2e2015-07-13 09:08:23 -0400955 prime = self['algorithm']['parameters']['p'].native
wbondf4f176c2015-07-07 23:27:19 -0400956 self._bit_size = int(math.ceil(math.log(prime, 2)))
wbondf937f482015-06-19 01:51:11 -0400957
958 return self._bit_size
959
960 @property
wbondf4f176c2015-07-07 23:27:19 -0400961 def byte_size(self):
962 """
963 :return:
964 The byte size of the public key, as an integer
965 """
966
967 return int(math.ceil(self.bit_size / 8))
968
969 @property
wbonde91513e2015-06-03 14:52:18 -0400970 def fingerprint(self):
971 """
972 Creates a fingerprint that can be compared with a private key to see if
973 the two form a pair.
974
975 This fingerprint is not compatiable with fingerprints generated by any
976 other software.
977
978 :return:
979 A byte string that is a sha256 hash of selected components (based
980 on the key type)
981 """
982
983 if self._fingerprint is None:
984 key_type = self['algorithm']['algorithm'].native
wbondeb02d762015-06-17 23:37:30 -0400985 params = self['algorithm']['parameters']
wbonde91513e2015-06-03 14:52:18 -0400986
987 if key_type == 'rsa':
wbondeb02d762015-06-17 23:37:30 -0400988 key = self['public_key'].parsed
wbonde91513e2015-06-03 14:52:18 -0400989 to_hash = '%d:%d' % (
990 key['modulus'].native,
991 key['public_exponent'].native,
992 )
993
994 elif key_type == 'dsa':
wbondeb02d762015-06-17 23:37:30 -0400995 key = self['public_key'].parsed
wbonde91513e2015-06-03 14:52:18 -0400996 to_hash = '%d:%d:%d:%d' % (
997 params['p'].native,
998 params['q'].native,
999 params['g'].native,
1000 key.native,
1001 )
1002
wbond680cba12015-07-01 23:53:54 -04001003 elif key_type == 'ec':
wbondeb02d762015-06-17 23:37:30 -04001004 key = self['public_key']
1005
wbonde91513e2015-06-03 14:52:18 -04001006 if params.name == 'named':
wbondeb02d762015-06-17 23:37:30 -04001007 to_hash = '%s:' % params.chosen.native
1008 to_hash = to_hash.encode('utf-8')
1009 to_hash += key.native
wbonde91513e2015-06-03 14:52:18 -04001010
wbondeb02d762015-06-17 23:37:30 -04001011 elif params.name == 'implicit_ca':
1012 to_hash = key.native
wbonde91513e2015-06-03 14:52:18 -04001013
wbondeb02d762015-06-17 23:37:30 -04001014 elif params.name == 'specified':
1015 to_hash = '%s:' % params.chosen['field_id']['parameters'].native
1016 to_hash = to_hash.encode('utf-8')
1017 to_hash += b':' + params.chosen['curve']['a'].native
1018 to_hash += b':' + params.chosen['curve']['b'].native
1019 to_hash += key.native
wbonde91513e2015-06-03 14:52:18 -04001020
1021 if isinstance(to_hash, str_cls):
1022 to_hash = to_hash.encode('utf-8')
1023
1024 self._fingerprint = hashlib.sha256(to_hash).digest()
1025
1026 return self._fingerprint