blob: e853094e0519088d0ba8b32826d808e9328580d5 [file] [log] [blame]
Jean-Paul Calderone8b63d452008-03-21 18:31:12 -04001# Copyright (C) Jean-Paul Calderone 2008, All rights reserved
2
Jean-Paul Calderoned8782ad2008-03-04 23:39:59 -05003"""
4Unit tests for L{OpenSSL.crypto}.
5"""
6
7from unittest import TestCase
8
9from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
Jean-Paul Calderone78381d22008-03-06 23:35:22 -050010from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
Jean-Paul Calderone2aa2b332008-03-06 21:43:14 -050011from OpenSSL.crypto import X509Req, X509ReqType
Jean-Paul Calderoned8782ad2008-03-04 23:39:59 -050012
Jean-Paul Calderoneac930e12008-03-06 18:50:51 -050013class _Python23TestCaseHelper:
Jean-Paul Calderone7da26a72008-03-06 00:35:20 -050014 # Python 2.3 compatibility.
15 def assertTrue(self, *a, **kw):
16 return self.failUnless(*a, **kw)
17
18
Jean-Paul Calderone7535dab2008-03-06 18:53:11 -050019 def assertFalse(self, *a, **kw):
20 return self.failIf(*a, **kw)
21
22
23
Jean-Paul Calderoneac930e12008-03-06 18:50:51 -050024class PKeyTests(TestCase, _Python23TestCaseHelper):
25 """
26 Unit tests for L{OpenSSL.crypto.PKey}.
27 """
Jean-Paul Calderoned8782ad2008-03-04 23:39:59 -050028 def test_construction(self):
29 """
30 L{PKey} takes no arguments and returns a new L{PKeyType} instance.
31 """
32 self.assertRaises(TypeError, PKey, None)
33 key = PKey()
34 self.assertTrue(
35 isinstance(key, PKeyType),
36 "%r is of type %r, should be %r" % (key, type(key), PKeyType))
37
38
39 def test_pregeneration(self):
40 """
41 L{PKeyType.bits} and L{PKeyType.type} return C{0} before the key is
42 generated.
43 """
44 key = PKey()
45 self.assertEqual(key.type(), 0)
46 self.assertEqual(key.bits(), 0)
47
48
49 def test_failedGeneration(self):
50 """
Jean-Paul Calderoneab82db72008-03-06 00:09:31 -050051 L{PKeyType.generate_key} takes two arguments, the first giving the key
52 type as one of L{TYPE_RSA} or L{TYPE_DSA} and the second giving the
53 number of bits to generate. If an invalid type is specified or
54 generation fails, L{Error} is raised. If an invalid number of bits is
55 specified, L{ValueError} or L{Error} is raised.
Jean-Paul Calderoned8782ad2008-03-04 23:39:59 -050056 """
57 key = PKey()
58 self.assertRaises(TypeError, key.generate_key)
59 self.assertRaises(TypeError, key.generate_key, 1, 2, 3)
60 self.assertRaises(TypeError, key.generate_key, "foo", "bar")
61 self.assertRaises(Error, key.generate_key, -1, 0)
Jean-Paul Calderoneab82db72008-03-06 00:09:31 -050062
Jean-Paul Calderoneab82db72008-03-06 00:09:31 -050063 self.assertRaises(ValueError, key.generate_key, TYPE_RSA, -1)
64 self.assertRaises(ValueError, key.generate_key, TYPE_RSA, 0)
Jean-Paul Calderoned71fe982008-03-06 00:31:50 -050065
66 # XXX RSA generation for small values of bits is fairly buggy in a wide
67 # range of OpenSSL versions. I need to figure out what the safe lower
68 # bound for a reasonable number of OpenSSL versions is and explicitly
69 # check for that in the wrapper. The failure behavior is typically an
70 # infinite loop inside OpenSSL.
71
72 # self.assertRaises(Error, key.generate_key, TYPE_RSA, 2)
Jean-Paul Calderoned8782ad2008-03-04 23:39:59 -050073
74 # XXX DSA generation seems happy with any number of bits. The DSS
75 # says bits must be between 512 and 1024 inclusive. OpenSSL's DSA
76 # generator doesn't seem to care about the upper limit at all. For
Jean-Paul Calderoneeff3cd92008-03-05 22:35:26 -050077 # the lower limit, it uses 512 if anything smaller is specified.
Jean-Paul Calderoned8782ad2008-03-04 23:39:59 -050078 # So, it doesn't seem possible to make generate_key fail for
79 # TYPE_DSA with a bits argument which is at least an int.
80
81 # self.assertRaises(Error, key.generate_key, TYPE_DSA, -7)
82
83
84 def test_rsaGeneration(self):
85 """
86 L{PKeyType.generate_key} generates an RSA key when passed
87 L{TYPE_RSA} as a type and a reasonable number of bits.
88 """
89 bits = 128
90 key = PKey()
91 key.generate_key(TYPE_RSA, bits)
92 self.assertEqual(key.type(), TYPE_RSA)
93 self.assertEqual(key.bits(), bits)
94
95
96 def test_dsaGeneration(self):
97 """
98 L{PKeyType.generate_key} generates a DSA key when passed
99 L{TYPE_DSA} as a type and a reasonable number of bits.
100 """
101 # 512 is a magic number. The DSS (Digital Signature Standard)
102 # allows a minimum of 512 bits for DSA. DSA_generate_parameters
103 # will silently promote any value below 512 to 512.
104 bits = 512
105 key = PKey()
106 key.generate_key(TYPE_DSA, bits)
107 self.assertEqual(key.type(), TYPE_DSA)
108 self.assertEqual(key.bits(), bits)
109
110
111 def test_regeneration(self):
112 """
113 L{PKeyType.generate_key} can be called multiple times on the same
114 key to generate new keys.
115 """
116 key = PKey()
117 for type, bits in [(TYPE_RSA, 512), (TYPE_DSA, 576)]:
118 key.generate_key(type, bits)
119 self.assertEqual(key.type(), type)
120 self.assertEqual(key.bits(), bits)
Jean-Paul Calderoneeff3cd92008-03-05 22:35:26 -0500121
122
123
Jean-Paul Calderoneac930e12008-03-06 18:50:51 -0500124class X509NameTests(TestCase, _Python23TestCaseHelper):
Jean-Paul Calderoneeff3cd92008-03-05 22:35:26 -0500125 """
126 Unit tests for L{OpenSSL.crypto.X509Name}.
127 """
Jean-Paul Calderonee098dc72008-03-06 18:36:19 -0500128 def _x509name(self, **attrs):
129 # XXX There's no other way to get a new X509Name yet.
130 name = X509().get_subject()
131 attrs = attrs.items()
132 # Make the order stable - order matters!
133 attrs.sort(lambda (k1, v1), (k2, v2): cmp(v1, v2))
134 for k, v in attrs:
135 setattr(name, k, v)
136 return name
137
138
Jean-Paul Calderoneeff3cd92008-03-05 22:35:26 -0500139 def test_attributes(self):
140 """
141 L{X509NameType} instances have attributes for each standard (?)
142 X509Name field.
143 """
Jean-Paul Calderonee098dc72008-03-06 18:36:19 -0500144 name = self._x509name()
Jean-Paul Calderoneeff3cd92008-03-05 22:35:26 -0500145 name.commonName = "foo"
146 self.assertEqual(name.commonName, "foo")
147 self.assertEqual(name.CN, "foo")
148 name.CN = "baz"
149 self.assertEqual(name.commonName, "baz")
150 self.assertEqual(name.CN, "baz")
151 name.commonName = "bar"
152 self.assertEqual(name.commonName, "bar")
153 self.assertEqual(name.CN, "bar")
154 name.CN = "quux"
155 self.assertEqual(name.commonName, "quux")
156 self.assertEqual(name.CN, "quux")
157
158
Jean-Paul Calderoneeff3cd92008-03-05 22:35:26 -0500159 def test_copy(self):
160 """
161 L{X509Name} creates a new L{X509NameType} instance with all the same
162 attributes as an existing L{X509NameType} instance when called with
163 one.
164 """
Jean-Paul Calderonee098dc72008-03-06 18:36:19 -0500165 name = self._x509name(commonName="foo", emailAddress="bar@example.com")
Jean-Paul Calderoneeff3cd92008-03-05 22:35:26 -0500166
167 copy = X509Name(name)
168 self.assertEqual(copy.commonName, "foo")
169 self.assertEqual(copy.emailAddress, "bar@example.com")
Jean-Paul Calderonee098dc72008-03-06 18:36:19 -0500170
171 # Mutate the copy and ensure the original is unmodified.
Jean-Paul Calderoneeff3cd92008-03-05 22:35:26 -0500172 copy.commonName = "baz"
173 self.assertEqual(name.commonName, "foo")
Jean-Paul Calderonee098dc72008-03-06 18:36:19 -0500174
175 # Mutate the original and ensure the copy is unmodified.
Jean-Paul Calderoneeff3cd92008-03-05 22:35:26 -0500176 name.emailAddress = "quux@example.com"
177 self.assertEqual(copy.emailAddress, "bar@example.com")
178
Jean-Paul Calderonee098dc72008-03-06 18:36:19 -0500179
180 def test_repr(self):
181 """
182 L{repr} passed an L{X509NameType} instance should return a string
183 containing a description of the type and the NIDs which have been set
184 on it.
185 """
186 name = self._x509name(commonName="foo", emailAddress="bar")
187 self.assertEqual(
188 repr(name),
189 "<X509Name object '/emailAddress=bar/CN=foo'>")
190
191
192 def test_comparison(self):
193 """
194 L{X509NameType} instances should compare based on their NIDs.
195 """
196 def _equality(a, b, assertTrue, assertFalse):
197 assertTrue(a == b, "(%r == %r) --> False" % (a, b))
198 assertFalse(a != b)
199 assertTrue(b == a)
200 assertFalse(b != a)
201
202 def assertEqual(a, b):
203 _equality(a, b, self.assertTrue, self.assertFalse)
204
205 # Instances compare equal to themselves.
206 name = self._x509name()
207 assertEqual(name, name)
208
209 # Empty instances should compare equal to each other.
210 assertEqual(self._x509name(), self._x509name())
211
212 # Instances with equal NIDs should compare equal to each other.
213 assertEqual(self._x509name(commonName="foo"),
214 self._x509name(commonName="foo"))
215
216 # Instance with equal NIDs set using different aliases should compare
217 # equal to each other.
218 assertEqual(self._x509name(commonName="foo"),
219 self._x509name(CN="foo"))
220
221 # Instances with more than one NID with the same values should compare
222 # equal to each other.
223 assertEqual(self._x509name(CN="foo", organizationalUnitName="bar"),
224 self._x509name(commonName="foo", OU="bar"))
225
226 def assertNotEqual(a, b):
227 _equality(a, b, self.assertFalse, self.assertTrue)
228
229 # Instances with different values for the same NID should not compare
230 # equal to each other.
231 assertNotEqual(self._x509name(CN="foo"),
232 self._x509name(CN="bar"))
233
234 # Instances with different NIDs should not compare equal to each other.
235 assertNotEqual(self._x509name(CN="foo"),
236 self._x509name(OU="foo"))
237
238 def _inequality(a, b, assertTrue, assertFalse):
239 assertTrue(a < b)
240 assertTrue(a <= b)
241 assertTrue(b > a)
242 assertTrue(b >= a)
243 assertFalse(a > b)
244 assertFalse(a >= b)
245 assertFalse(b < a)
246 assertFalse(b <= a)
247
248 def assertLessThan(a, b):
249 _inequality(a, b, self.assertTrue, self.assertFalse)
250
251 # An X509Name with a NID with a value which sorts less than the value
252 # of the same NID on another X509Name compares less than the other
253 # X509Name.
254 assertLessThan(self._x509name(CN="abc"),
255 self._x509name(CN="def"))
256
257 def assertGreaterThan(a, b):
258 _inequality(a, b, self.assertFalse, self.assertTrue)
259
260 # An X509Name with a NID with a value which sorts greater than the
261 # value of the same NID on another X509Name compares greater than the
262 # other X509Name.
263 assertGreaterThan(self._x509name(CN="def"),
264 self._x509name(CN="abc"))
Jean-Paul Calderone2aa2b332008-03-06 21:43:14 -0500265
266
267
Jean-Paul Calderone110cd092008-03-24 17:27:42 -0400268 def test_hash(self):
269 """
270 L{X509Name.hash} returns an integer hash based on the value of the
271 name.
272 """
273 a = self._x509name(CN="foo")
274 b = self._x509name(CN="foo")
275 self.assertEqual(a.hash(), b.hash())
276 a.CN = "bar"
277 self.assertNotEqual(a.hash(), b.hash())
278
279
280
Jean-Paul Calderoneac0d95f2008-03-10 00:00:42 -0400281class _PKeyInteractionTestsMixin:
282 """
283 Tests which involve another thing and a PKey.
284 """
285 def signable(self):
286 """
287 Return something with a C{set_pubkey}, C{set_pubkey}, and C{sign} method.
288 """
289 raise NotImplementedError()
290
291
292 def test_signWithUngenerated(self):
293 """
294 L{X509Req.sign} raises L{ValueError} when pass a L{PKey} with no parts.
295 """
296 request = self.signable()
297 key = PKey()
298 self.assertRaises(ValueError, request.sign, key, 'MD5')
299
300
301 def test_signWithPublicKey(self):
302 """
303 L{X509Req.sign} raises L{ValueError} when pass a L{PKey} with no
304 private part as the signing key.
305 """
306 request = self.signable()
307 key = PKey()
308 key.generate_key(TYPE_RSA, 512)
309 request.set_pubkey(key)
310 pub = request.get_pubkey()
311 self.assertRaises(ValueError, request.sign, pub, 'MD5')
312
313
314
315class X509ReqTests(TestCase, _PKeyInteractionTestsMixin, _Python23TestCaseHelper):
Jean-Paul Calderone2aa2b332008-03-06 21:43:14 -0500316 """
317 Tests for L{OpenSSL.crypto.X509Req}.
318 """
Jean-Paul Calderoneac0d95f2008-03-10 00:00:42 -0400319 def signable(self):
320 """
321 Create and return a new L{X509Req}.
322 """
323 return X509Req()
324
325
Jean-Paul Calderone2aa2b332008-03-06 21:43:14 -0500326 def test_construction(self):
327 """
328 L{X509Req} takes no arguments and returns an L{X509ReqType} instance.
329 """
330 request = X509Req()
331 self.assertTrue(
332 isinstance(request, X509ReqType),
333 "%r is of type %r, should be %r" % (request, type(request), X509ReqType))
334
335
336 def test_get_subject(self):
337 """
338 L{X509ReqType.get_subject} returns an L{X509Name} for the subject of
339 the request and which is valid even after the request object is
340 otherwise dead.
341 """
342 request = X509Req()
343 subject = request.get_subject()
344 self.assertTrue(
345 isinstance(subject, X509NameType),
346 "%r is of type %r, should be %r" % (subject, type(subject), X509NameType))
347 subject.commonName = "foo"
348 self.assertEqual(request.get_subject().commonName, "foo")
349 del request
350 subject.commonName = "bar"
351 self.assertEqual(subject.commonName, "bar")
Jean-Paul Calderone78381d22008-03-06 23:35:22 -0500352
353
354
Jean-Paul Calderoneac0d95f2008-03-10 00:00:42 -0400355class X509Tests(TestCase, _PKeyInteractionTestsMixin, _Python23TestCaseHelper):
Jean-Paul Calderone78381d22008-03-06 23:35:22 -0500356 """
357 Tests for L{OpenSSL.crypto.X509}.
358 """
Jean-Paul Calderoneac0d95f2008-03-10 00:00:42 -0400359 def signable(self):
360 """
361 Create and return a new L{X509}.
362 """
363 return X509()
364
365
Jean-Paul Calderone78381d22008-03-06 23:35:22 -0500366 def test_construction(self):
367 """
368 L{X509} takes no arguments and returns an instance of L{X509Type}.
369 """
370 certificate = X509()
371 self.assertTrue(
372 isinstance(certificate, X509Type),
373 "%r is of type %r, should be %r" % (certificate,
374 type(certificate),
375 X509Type))
376
377
378 def test_serial_number(self):
379 """
380 The serial number of an L{X509Type} can be retrieved and modified with
381 L{X509Type.get_serial_number} and L{X509Type.set_serial_number}.
382 """
383 certificate = X509()
384 self.assertRaises(TypeError, certificate.set_serial_number)
385 self.assertRaises(TypeError, certificate.set_serial_number, 1, 2)
386 self.assertRaises(TypeError, certificate.set_serial_number, "1")
387 self.assertRaises(TypeError, certificate.set_serial_number, 5.5)
388 self.assertEqual(certificate.get_serial_number(), 0)
389 certificate.set_serial_number(1)
390 self.assertEqual(certificate.get_serial_number(), 1)
391 certificate.set_serial_number(2 ** 32 + 1)
392 self.assertEqual(certificate.get_serial_number(), 2 ** 32 + 1)
393 certificate.set_serial_number(2 ** 64 + 1)
394 self.assertEqual(certificate.get_serial_number(), 2 ** 64 + 1)
Jean-Paul Calderone525ef802008-03-09 20:39:42 -0400395 certificate.set_serial_number(2 ** 128 + 1)
396 self.assertEqual(certificate.get_serial_number(), 2 ** 128 + 1)
397
398
399 def _setBoundTest(self, which):
400 """
401 L{X509Type.set_notBefore} takes a string in the format of an ASN1
402 GENERALIZEDTIME and sets the beginning of the certificate's validity
403 period to it.
404 """
405 certificate = X509()
406 set = getattr(certificate, 'set_not' + which)
407 get = getattr(certificate, 'get_not' + which)
408
Jean-Paul Calderonee0615b52008-03-09 21:44:46 -0400409 # Starts with no value.
410 self.assertEqual(get(), None)
411
Jean-Paul Calderone525ef802008-03-09 20:39:42 -0400412 # GMT (Or is it UTC?) -exarkun
413 when = "20040203040506Z"
414 set(when)
415 self.assertEqual(get(), when)
416
417 # A plus two hours and thirty minutes offset
418 when = "20040203040506+0530"
419 set(when)
420 self.assertEqual(get(), when)
421
422 # A minus one hour fifteen minutes offset
423 when = "20040203040506-0115"
424 set(when)
425 self.assertEqual(get(), when)
426
427 # An invalid string results in a ValueError
428 self.assertRaises(ValueError, set, "foo bar")
429
430
431 def test_set_notBefore(self):
432 """
433 L{X509Type.set_notBefore} takes a string in the format of an ASN1
434 GENERALIZEDTIME and sets the beginning of the certificate's validity
435 period to it.
436 """
437 self._setBoundTest("Before")
438
439
440 def test_set_notAfter(self):
441 """
442 L{X509Type.set_notAfter} takes a string in the format of an ASN1
443 GENERALIZEDTIME and sets the end of the certificate's validity period
444 to it.
445 """
446 self._setBoundTest("After")
Jean-Paul Calderone76576d52008-03-24 16:04:46 -0400447
448
449 def test_digest(self):
450 """
451 L{X509.digest} returns a string giving ":"-separated hex-encoded words
452 of the digest of the certificate.
453 """
454 cert = X509()
455 self.assertEqual(
456 cert.digest("md5"),
457 "A8:EB:07:F8:53:25:0A:F2:56:05:C5:A5:C4:C4:C7:15")