Implement multi uid support
[keymanager.git] / src / leap / keymanager / tests / test_keymanager.py
1 # -*- coding: utf-8 -*-
2 # test_keymanager.py
3 # Copyright (C) 2013 LEAP
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18
19 """
20 Tests for the Key Manager.
21 """
22
23
24 from datetime import datetime
25 from mock import Mock
26 from leap.common.testing.basetest import BaseLeapTest
27 from leap.keymanager import (
28     openpgp,
29     KeyNotFound,
30     KeyAddressMismatch,
31     errors,
32 )
33 from leap.keymanager.openpgp import OpenPGPKey
34 from leap.keymanager.keys import (
35     is_address,
36     build_key_from_dict,
37 )
38 from leap.keymanager.validation import (
39     ValidationLevel,
40     toValidationLevel
41 )
42 from leap.keymanager.tests import (
43     KeyManagerWithSoledadTestCase,
44     ADDRESS,
45     KEY_FINGERPRINT,
46     PUBLIC_KEY,
47     PRIVATE_KEY,
48     GPG_BINARY_PATH
49 )
50
51
52 ADDRESS_2 = 'anotheruser@leap.se'
53
54
55 class KeyManagerUtilTestCase(BaseLeapTest):
56
57     def setUp(self):
58         pass
59
60     def tearDown(self):
61         pass
62
63     def test_is_address(self):
64         self.assertTrue(
65             is_address('user@leap.se'),
66             'Incorrect address detection.')
67         self.assertFalse(
68             is_address('userleap.se'),
69             'Incorrect address detection.')
70         self.assertFalse(
71             is_address('user@'),
72             'Incorrect address detection.')
73         self.assertFalse(
74             is_address('@leap.se'),
75             'Incorrect address detection.')
76
77     def test_build_key_from_dict(self):
78         kdict = {
79             'address': [ADDRESS],
80             'key_id': KEY_FINGERPRINT[-16:],
81             'fingerprint': KEY_FINGERPRINT,
82             'key_data': PUBLIC_KEY,
83             'private': False,
84             'length': 4096,
85             'expiry_date': 0,
86             'last_audited_at': 0,
87             'refreshed_at': 1311239602,
88             'validation': str(ValidationLevel.Weak_Chain),
89             'encr_used': False,
90             'sign_used': True,
91         }
92         key = build_key_from_dict(OpenPGPKey, kdict)
93         self.assertEqual(
94             kdict['address'], key.address,
95             'Wrong data in key.')
96         self.assertEqual(
97             kdict['key_id'], key.key_id,
98             'Wrong data in key.')
99         self.assertEqual(
100             kdict['fingerprint'], key.fingerprint,
101             'Wrong data in key.')
102         self.assertEqual(
103             kdict['key_data'], key.key_data,
104             'Wrong data in key.')
105         self.assertEqual(
106             kdict['private'], key.private,
107             'Wrong data in key.')
108         self.assertEqual(
109             kdict['length'], key.length,
110             'Wrong data in key.')
111         self.assertEqual(
112             None, key.expiry_date,
113             'Wrong data in key.')
114         self.assertEqual(
115             None, key.last_audited_at,
116             'Wrong data in key.')
117         self.assertEqual(
118             datetime.fromtimestamp(kdict['refreshed_at']), key.refreshed_at,
119             'Wrong data in key.')
120         self.assertEqual(
121             toValidationLevel(kdict['validation']), key.validation,
122             'Wrong data in key.')
123         self.assertEqual(
124             kdict['encr_used'], key.encr_used,
125             'Wrong data in key.')
126         self.assertEqual(
127             kdict['sign_used'], key.sign_used,
128             'Wrong data in key.')
129
130
131 class OpenPGPCryptoTestCase(KeyManagerWithSoledadTestCase):
132
133     def _test_openpgp_gen_key(self):
134         pgp = openpgp.OpenPGPScheme(self._soledad)
135         self.assertRaises(KeyNotFound, pgp.get_key, 'user@leap.se')
136         key = pgp.gen_key('user@leap.se')
137         self.assertIsInstance(key, openpgp.OpenPGPKey)
138         self.assertEqual(
139             ['user@leap.se'], key.address, 'Wrong address bound to key.')
140         self.assertEqual(
141             4096, key.length, 'Wrong key length.')
142
143     def test_openpgp_put_delete_key(self):
144         pgp = openpgp.OpenPGPScheme(
145             self._soledad, gpgbinary=GPG_BINARY_PATH)
146         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
147         pgp.put_ascii_key(PUBLIC_KEY, ADDRESS)
148         key = pgp.get_key(ADDRESS, private=False)
149         pgp.delete_key(key)
150         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
151
152     def test_openpgp_put_ascii_key(self):
153         pgp = openpgp.OpenPGPScheme(
154             self._soledad, gpgbinary=GPG_BINARY_PATH)
155         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
156         pgp.put_ascii_key(PUBLIC_KEY, ADDRESS)
157         key = pgp.get_key(ADDRESS, private=False)
158         self.assertIsInstance(key, openpgp.OpenPGPKey)
159         self.assertTrue(
160             ADDRESS in key.address, 'Wrong address bound to key.')
161         self.assertEqual(
162             4096, key.length, 'Wrong key length.')
163         pgp.delete_key(key)
164         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
165
166     def test_get_public_key(self):
167         pgp = openpgp.OpenPGPScheme(
168             self._soledad, gpgbinary=GPG_BINARY_PATH)
169         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
170         pgp.put_ascii_key(PUBLIC_KEY, ADDRESS)
171         self.assertRaises(
172             KeyNotFound, pgp.get_key, ADDRESS, private=True)
173         key = pgp.get_key(ADDRESS, private=False)
174         self.assertTrue(ADDRESS in key.address)
175         self.assertFalse(key.private)
176         self.assertEqual(KEY_FINGERPRINT, key.fingerprint)
177         pgp.delete_key(key)
178         self.assertRaises(KeyNotFound, pgp.get_key, ADDRESS)
179
180     def test_openpgp_encrypt_decrypt(self):
181         # encrypt
182         pgp = openpgp.OpenPGPScheme(
183             self._soledad, gpgbinary=GPG_BINARY_PATH)
184         pgp.put_ascii_key(PUBLIC_KEY, ADDRESS)
185         pubkey = pgp.get_key(ADDRESS, private=False)
186         cyphertext = pgp.encrypt('data', pubkey)
187         # assert
188         self.assertTrue(cyphertext is not None)
189         self.assertTrue(cyphertext != '')
190         self.assertTrue(cyphertext != 'data')
191         self.assertTrue(pgp.is_encrypted(cyphertext))
192         self.assertTrue(pgp.is_encrypted(cyphertext))
193         # decrypt
194         self.assertRaises(
195             KeyNotFound, pgp.get_key, ADDRESS, private=True)
196         pgp.put_ascii_key(PRIVATE_KEY, ADDRESS)
197         privkey = pgp.get_key(ADDRESS, private=True)
198         pgp.delete_key(pubkey)
199         pgp.delete_key(privkey)
200         self.assertRaises(
201             KeyNotFound, pgp.get_key, ADDRESS, private=False)
202         self.assertRaises(
203             KeyNotFound, pgp.get_key, ADDRESS, private=True)
204
205     def test_verify_with_private_raises(self):
206         pgp = openpgp.OpenPGPScheme(
207             self._soledad, gpgbinary=GPG_BINARY_PATH)
208         pgp.put_ascii_key(PRIVATE_KEY)
209         data = 'data'
210         privkey = pgp.get_key(ADDRESS, private=True)
211         signed = pgp.sign(data, privkey)
212         self.assertRaises(
213             AssertionError,
214             pgp.verify, signed, privkey)
215
216     def test_sign_with_public_raises(self):
217         pgp = openpgp.OpenPGPScheme(
218             self._soledad, gpgbinary=GPG_BINARY_PATH)
219         pgp.put_ascii_key(PUBLIC_KEY, ADDRESS)
220         data = 'data'
221         pubkey = pgp.get_key(ADDRESS, private=False)
222         self.assertRaises(
223             AssertionError,
224             pgp.sign, data, pubkey)
225
226     def test_verify_with_wrong_key_raises(self):
227         pgp = openpgp.OpenPGPScheme(
228             self._soledad, gpgbinary=GPG_BINARY_PATH)
229         pgp.put_ascii_key(PRIVATE_KEY, ADDRESS)
230         data = 'data'
231         privkey = pgp.get_key(ADDRESS, private=True)
232         signed = pgp.sign(data, privkey)
233         pgp.put_ascii_key(PUBLIC_KEY_2, ADDRESS_2)
234         wrongkey = pgp.get_key(ADDRESS_2)
235         self.assertRaises(
236             errors.InvalidSignature,
237             pgp.verify, signed, wrongkey)
238
239     def test_encrypt_sign_with_public_raises(self):
240         pgp = openpgp.OpenPGPScheme(
241             self._soledad, gpgbinary=GPG_BINARY_PATH)
242         pgp.put_ascii_key(PRIVATE_KEY, ADDRESS)
243         data = 'data'
244         privkey = pgp.get_key(ADDRESS, private=True)
245         pubkey = pgp.get_key(ADDRESS, private=False)
246         self.assertRaises(
247             AssertionError,
248             pgp.encrypt, data, privkey, sign=pubkey)
249
250     def test_decrypt_verify_with_private_raises(self):
251         pgp = openpgp.OpenPGPScheme(
252             self._soledad, gpgbinary=GPG_BINARY_PATH)
253         pgp.put_ascii_key(PRIVATE_KEY, ADDRESS)
254         data = 'data'
255         privkey = pgp.get_key(ADDRESS, private=True)
256         pubkey = pgp.get_key(ADDRESS, private=False)
257         encrypted_and_signed = pgp.encrypt(
258             data, pubkey, sign=privkey)
259         self.assertRaises(
260             AssertionError,
261             pgp.decrypt,
262             encrypted_and_signed, privkey, verify=privkey)
263
264     def test_decrypt_verify_with_wrong_key_raises(self):
265         pgp = openpgp.OpenPGPScheme(
266             self._soledad, gpgbinary=GPG_BINARY_PATH)
267         pgp.put_ascii_key(PRIVATE_KEY, ADDRESS)
268         data = 'data'
269         privkey = pgp.get_key(ADDRESS, private=True)
270         pubkey = pgp.get_key(ADDRESS, private=False)
271         encrypted_and_signed = pgp.encrypt(data, pubkey, sign=privkey)
272         pgp.put_ascii_key(PUBLIC_KEY_2, ADDRESS_2)
273         wrongkey = pgp.get_key(ADDRESS_2)
274         self.assertRaises(
275             errors.InvalidSignature,
276             pgp.verify, encrypted_and_signed, wrongkey)
277
278     def test_sign_verify(self):
279         pgp = openpgp.OpenPGPScheme(
280             self._soledad, gpgbinary=GPG_BINARY_PATH)
281         pgp.put_ascii_key(PRIVATE_KEY, ADDRESS)
282         data = 'data'
283         privkey = pgp.get_key(ADDRESS, private=True)
284         signed = pgp.sign(data, privkey, detach=False)
285         pubkey = pgp.get_key(ADDRESS, private=False)
286         self.assertTrue(pgp.verify(signed, pubkey))
287
288     def test_encrypt_sign_decrypt_verify(self):
289         pgp = openpgp.OpenPGPScheme(
290             self._soledad, gpgbinary=GPG_BINARY_PATH)
291         pgp.put_ascii_key(PRIVATE_KEY, ADDRESS)
292         pubkey = pgp.get_key(ADDRESS, private=False)
293         privkey = pgp.get_key(ADDRESS, private=True)
294         pgp.put_ascii_key(PRIVATE_KEY_2, ADDRESS_2)
295         pubkey2 = pgp.get_key(ADDRESS_2, private=False)
296         privkey2 = pgp.get_key(ADDRESS_2, private=True)
297         data = 'data'
298         encrypted_and_signed = pgp.encrypt(
299             data, pubkey2, sign=privkey)
300         res = pgp.decrypt(
301             encrypted_and_signed, privkey2, verify=pubkey)
302         self.assertTrue(data, res)
303
304     def test_sign_verify_detached_sig(self):
305         pgp = openpgp.OpenPGPScheme(
306             self._soledad, gpgbinary=GPG_BINARY_PATH)
307         pgp.put_ascii_key(PRIVATE_KEY, ADDRESS)
308         data = 'data'
309         privkey = pgp.get_key(ADDRESS, private=True)
310         signature = pgp.sign(data, privkey, detach=True)
311         pubkey = pgp.get_key(ADDRESS, private=False)
312         self.assertTrue(pgp.verify(data, pubkey, detached_sig=signature))
313
314
315 class KeyManagerKeyManagementTestCase(KeyManagerWithSoledadTestCase):
316
317     def test_get_all_keys_in_db(self):
318         km = self._key_manager()
319         km._wrapper_map[OpenPGPKey].put_ascii_key(PRIVATE_KEY, ADDRESS)
320         # get public keys
321         keys = km.get_all_keys(False)
322         self.assertEqual(len(keys), 1, 'Wrong number of keys')
323         self.assertTrue(ADDRESS in keys[0].address)
324         self.assertFalse(keys[0].private)
325         # get private keys
326         keys = km.get_all_keys(True)
327         self.assertEqual(len(keys), 1, 'Wrong number of keys')
328         self.assertTrue(ADDRESS in keys[0].address)
329         self.assertTrue(keys[0].private)
330
331     def test_get_public_key(self):
332         km = self._key_manager()
333         km._wrapper_map[OpenPGPKey].put_ascii_key(PRIVATE_KEY, ADDRESS)
334         # get the key
335         key = km.get_key(ADDRESS, OpenPGPKey, private=False,
336                          fetch_remote=False)
337         self.assertTrue(key is not None)
338         self.assertTrue(ADDRESS in key.address)
339         self.assertEqual(
340             key.fingerprint.lower(), KEY_FINGERPRINT.lower())
341         self.assertFalse(key.private)
342
343     def test_get_private_key(self):
344         km = self._key_manager()
345         km._wrapper_map[OpenPGPKey].put_ascii_key(PRIVATE_KEY, ADDRESS)
346         # get the key
347         key = km.get_key(ADDRESS, OpenPGPKey, private=True,
348                          fetch_remote=False)
349         self.assertTrue(key is not None)
350         self.assertTrue(ADDRESS in key.address)
351         self.assertEqual(
352             key.fingerprint.lower(), KEY_FINGERPRINT.lower())
353         self.assertTrue(key.private)
354
355     def test_send_key_raises_key_not_found(self):
356         km = self._key_manager()
357         self.assertRaises(
358             KeyNotFound,
359             km.send_key, OpenPGPKey)
360
361     def test_send_key(self):
362         """
363         Test that request is well formed when sending keys to server.
364         """
365         token = "mytoken"
366         km = self._key_manager(token=token)
367         km._wrapper_map[OpenPGPKey].put_ascii_key(PUBLIC_KEY, ADDRESS)
368         km._fetcher.put = Mock()
369         # the following data will be used on the send
370         km.ca_cert_path = 'capath'
371         km.session_id = 'sessionid'
372         km.uid = 'myuid'
373         km.api_uri = 'apiuri'
374         km.api_version = 'apiver'
375         km.send_key(OpenPGPKey)
376         # setup expected args
377         data = {
378             km.PUBKEY_KEY: km.get_key(km._address, OpenPGPKey).key_data,
379         }
380         url = '%s/%s/users/%s.json' % ('apiuri', 'apiver', 'myuid')
381         km._fetcher.put.assert_called_once_with(
382             url, data=data, verify='capath',
383             headers={'Authorization': 'Token token=%s' % token},
384         )
385
386     def test__fetch_keys_from_server(self):
387         """
388         Test that the request is well formed when fetching keys from server.
389         """
390         km = self._key_manager(url='http://nickserver.domain')
391
392         class Response(object):
393             status_code = 200
394             headers = {'content-type': 'application/json'}
395
396             def json(self):
397                 return {'address': ADDRESS_2, 'openpgp': PUBLIC_KEY_2}
398
399             def raise_for_status(self):
400                 pass
401
402         # mock the fetcher so it returns the key for ADDRESS_2
403         km._fetcher.get = Mock(
404             return_value=Response())
405         km.ca_cert_path = 'cacertpath'
406         # do the fetch
407         km._fetch_keys_from_server(ADDRESS_2)
408         # and verify the call
409         km._fetcher.get.assert_called_once_with(
410             'http://nickserver.domain',
411             data={'address': ADDRESS_2},
412             verify='cacertpath',
413         )
414
415     def test_get_key_fetches_from_server(self):
416         """
417         Test that getting a key successfuly fetches from server.
418         """
419         km = self._key_manager(url='http://nickserver.domain')
420
421         class Response(object):
422             status_code = 200
423             headers = {'content-type': 'application/json'}
424
425             def json(self):
426                 return {'address': ADDRESS, 'openpgp': PUBLIC_KEY}
427
428             def raise_for_status(self):
429                 pass
430
431         # mock the fetcher so it returns the key for ADDRESS_2
432         km._fetcher.get = Mock(return_value=Response())
433         km.ca_cert_path = 'cacertpath'
434         # try to key get without fetching from server
435         self.assertRaises(
436             KeyNotFound, km.get_key, ADDRESS, OpenPGPKey,
437             fetch_remote=False
438         )
439         # try to get key fetching from server.
440         key = km.get_key(ADDRESS, OpenPGPKey)
441         self.assertIsInstance(key, OpenPGPKey)
442         self.assertTrue(ADDRESS in key.address)
443
444     def test_put_key_ascii(self):
445         """
446         Test that putting ascii key works
447         """
448         km = self._key_manager(url='http://nickserver.domain')
449
450         km.put_raw_key(PUBLIC_KEY, OpenPGPKey, ADDRESS)
451         key = km.get_key(ADDRESS, OpenPGPKey)
452         self.assertIsInstance(key, OpenPGPKey)
453         self.assertTrue(ADDRESS in key.address)
454
455     def test_fetch_uri_ascii_key(self):
456         """
457         Test that fetch key downloads the ascii key and gets included in
458         the local storage
459         """
460         km = self._key_manager()
461
462         class Response(object):
463             ok = True
464             content = PUBLIC_KEY
465
466         km._fetcher.get = Mock(return_value=Response())
467         km.ca_cert_path = 'cacertpath'
468
469         km.fetch_key(ADDRESS, "http://site.domain/key", OpenPGPKey)
470         key = km.get_key(ADDRESS, OpenPGPKey)
471         self.assertEqual(KEY_FINGERPRINT, key.fingerprint)
472
473     def test_fetch_uri_empty_key(self):
474         """
475         Test that fetch key raises KeyNotFound if no key in the url
476         """
477         km = self._key_manager()
478
479         class Response(object):
480             ok = True
481             content = ""
482
483         km._fetcher.get = Mock(return_value=Response())
484         km.ca_cert_path = 'cacertpath'
485         self.assertRaises(KeyNotFound, km.fetch_key,
486                           ADDRESS, "http://site.domain/key", OpenPGPKey)
487
488     def test_fetch_uri_address_differ(self):
489         """
490         Test that fetch key raises KeyAttributesDiffer if the address
491         don't match
492         """
493         km = self._key_manager()
494
495         class Response(object):
496             ok = True
497             content = PUBLIC_KEY
498
499         km._fetcher.get = Mock(return_value=Response())
500         km.ca_cert_path = 'cacertpath'
501         self.assertRaises(KeyAddressMismatch, km.fetch_key,
502                           ADDRESS_2, "http://site.domain/key", OpenPGPKey)
503
504
505 class KeyManagerCryptoTestCase(KeyManagerWithSoledadTestCase):
506
507     RAW_DATA = 'data'
508
509     def test_keymanager_openpgp_encrypt_decrypt(self):
510         km = self._key_manager()
511         # put raw private key
512         km._wrapper_map[OpenPGPKey].put_ascii_key(PRIVATE_KEY, ADDRESS)
513         # get public key
514         pubkey = km.get_key(
515             ADDRESS, OpenPGPKey, private=False, fetch_remote=False)
516         # encrypt
517         encdata = km.encrypt(self.RAW_DATA, pubkey)
518         self.assertNotEqual(self.RAW_DATA, encdata)
519         # get private key
520         privkey = km.get_key(
521             ADDRESS, OpenPGPKey, private=True, fetch_remote=False)
522         # decrypt
523         rawdata = km.decrypt(encdata, privkey)
524         self.assertEqual(self.RAW_DATA, rawdata)
525
526     def test_keymanager_openpgp_sign_verify(self):
527         km = self._key_manager()
528         # put raw private keys
529         km._wrapper_map[OpenPGPKey].put_ascii_key(PRIVATE_KEY, ADDRESS)
530         # get private key for signing
531         privkey = km.get_key(
532             ADDRESS, OpenPGPKey, private=True, fetch_remote=False)
533         # encrypt
534         signdata = km.sign(self.RAW_DATA, privkey, detach=False)
535         self.assertNotEqual(self.RAW_DATA, signdata)
536         # get public key for verifying
537         pubkey = km.get_key(
538             ADDRESS, OpenPGPKey, private=False, fetch_remote=False)
539         # decrypt
540         self.assertTrue(km.verify(signdata, pubkey))
541
542
543 # Key material for testing
544
545 # key 7FEE575A: public key "anotheruser <anotheruser@leap.se>"
546 PUBLIC_KEY_2 = """
547 -----BEGIN PGP PUBLIC KEY BLOCK-----
548 Version: GnuPG v1.4.10 (GNU/Linux)
549
550 mI0EUYwJXgEEAMbTKHuPJ5/Gk34l9Z06f+0WCXTDXdte1UBoDtZ1erAbudgC4MOR
551 gquKqoj3Hhw0/ILqJ88GcOJmKK/bEoIAuKaqlzDF7UAYpOsPZZYmtRfPC2pTCnXq
552 Z1vdeqLwTbUspqXflkCkFtfhGKMq5rH8GV5a3tXZkRWZhdNwhVXZagC3ABEBAAG0
553 IWFub3RoZXJ1c2VyIDxhbm90aGVydXNlckBsZWFwLnNlPoi4BBMBAgAiBQJRjAle
554 AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRB/nfpof+5XWotuA/4tLN4E
555 gUr7IfLy2HkHAxzw7A4rqfMN92DIM9mZrDGaWRrOn3aVF7VU1UG7MDkHfPvp/cFw
556 ezoCw4s4IoHVc/pVlOkcHSyt4/Rfh248tYEJmFCJXGHpkK83VIKYJAithNccJ6Q4
557 JE/o06Mtf4uh/cA1HUL4a4ceqUhtpLJULLeKo7iNBFGMCV4BBADsyQI7GR0wSAxz
558 VayLjuPzgT+bjbFeymIhjuxKIEwnIKwYkovztW+4bbOcQs785k3Lp6RzvigTpQQt
559 Z/hwcLOqZbZw8t/24+D+Pq9mMP2uUvCFFqLlVvA6D3vKSQ/XNN+YB919WQ04jh63
560 yuRe94WenT1RJd6xU1aaUff4rKizuQARAQABiJ8EGAECAAkFAlGMCV4CGwwACgkQ
561 f536aH/uV1rPZQQAqCzRysOlu8ez7PuiBD4SebgRqWlxa1TF1ujzfLmuPivROZ2X
562 Kw5aQstxgGSjoB7tac49s0huh4X8XK+BtJBfU84JS8Jc2satlfwoyZ35LH6sDZck
563 I+RS/3we6zpMfHs3vvp9xgca6ZupQxivGtxlJs294TpJorx+mFFqbV17AzQ=
564 =Thdu
565 -----END PGP PUBLIC KEY BLOCK-----
566 """
567
568 PRIVATE_KEY_2 = """
569 -----BEGIN PGP PRIVATE KEY BLOCK-----
570 Version: GnuPG v1.4.10 (GNU/Linux)
571
572 lQHYBFGMCV4BBADG0yh7jyefxpN+JfWdOn/tFgl0w13bXtVAaA7WdXqwG7nYAuDD
573 kYKriqqI9x4cNPyC6ifPBnDiZiiv2xKCALimqpcwxe1AGKTrD2WWJrUXzwtqUwp1
574 6mdb3Xqi8E21LKal35ZApBbX4RijKuax/BleWt7V2ZEVmYXTcIVV2WoAtwARAQAB
575 AAP7BLuSAx7tOohnimEs74ks8l/L6dOcsFQZj2bqs4AoY3jFe7bV0tHr4llypb/8
576 H3/DYvpf6DWnCjyUS1tTnXSW8JXtx01BUKaAufSmMNg9blKV6GGHlT/Whe9uVyks
577 7XHk/+9mebVMNJ/kNlqq2k+uWqJohzC8WWLRK+d1tBeqDsECANZmzltPaqUsGV5X
578 C3zszE3tUBgptV/mKnBtopKi+VH+t7K6fudGcG+bAcZDUoH/QVde52mIIjjIdLje
579 uajJuHUCAO1mqh+vPoGv4eBLV7iBo3XrunyGXiys4a39eomhxTy3YktQanjjx+ty
580 GltAGCs5PbWGO6/IRjjvd46wh53kzvsCAO0J97gsWhzLuFnkxFAJSPk7RRlyl7lI
581 1XS/x0Og6j9XHCyY1OYkfBm0to3UlCfkgirzCYlTYObCofzdKFIPDmSqHbQhYW5v
582 dGhlcnVzZXIgPGFub3RoZXJ1c2VyQGxlYXAuc2U+iLgEEwECACIFAlGMCV4CGwMG
583 CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEH+d+mh/7ldai24D/i0s3gSBSvsh
584 8vLYeQcDHPDsDiup8w33YMgz2ZmsMZpZGs6fdpUXtVTVQbswOQd8++n9wXB7OgLD
585 izgigdVz+lWU6RwdLK3j9F+Hbjy1gQmYUIlcYemQrzdUgpgkCK2E1xwnpDgkT+jT
586 oy1/i6H9wDUdQvhrhx6pSG2kslQst4qjnQHYBFGMCV4BBADsyQI7GR0wSAxzVayL
587 juPzgT+bjbFeymIhjuxKIEwnIKwYkovztW+4bbOcQs785k3Lp6RzvigTpQQtZ/hw
588 cLOqZbZw8t/24+D+Pq9mMP2uUvCFFqLlVvA6D3vKSQ/XNN+YB919WQ04jh63yuRe
589 94WenT1RJd6xU1aaUff4rKizuQARAQABAAP9EyElqJ3dq3EErXwwT4mMnbd1SrVC
590 rUJrNWQZL59mm5oigS00uIyR0SvusOr+UzTtd8ysRuwHy5d/LAZsbjQStaOMBILx
591 77TJveOel0a1QK0YSMF2ywZMCKvquvjli4hAtWYz/EwfuzQN3t23jc5ny+GqmqD2
592 3FUxLJosFUfLNmECAO9KhVmJi+L9dswIs+2Dkjd1eiRQzNOEVffvYkGYZyKxNiXF
593 UA5kvyZcB4iAN9sWCybE4WHZ9jd4myGB0MPDGxkCAP1RsXJbbuD6zS7BXe5gwunO
594 2q4q7ptdSl/sJYQuTe1KNP5d/uGsvlcFfsYjpsopasPjFBIncc/2QThMKlhoEaEB
595 /0mVAxpT6SrEvUbJ18z7kna24SgMPr3OnPMxPGfvNLJY/Xv/A17YfoqjmByCvsKE
596 JCDjopXtmbcrZyoEZbEht9mko4ifBBgBAgAJBQJRjAleAhsMAAoJEH+d+mh/7lda
597 z2UEAKgs0crDpbvHs+z7ogQ+Enm4EalpcWtUxdbo83y5rj4r0TmdlysOWkLLcYBk
598 o6Ae7WnOPbNIboeF/FyvgbSQX1POCUvCXNrGrZX8KMmd+Sx+rA2XJCPkUv98Hus6
599 THx7N776fcYHGumbqUMYrxrcZSbNveE6SaK8fphRam1dewM0
600 =a5gs
601 -----END PGP PRIVATE KEY BLOCK-----
602 """
603 import unittest
604 if __name__ == "__main__":
605     unittest.main()