fdbc206dba903086c4dad8c17818a909870d0cad
[keymanager.git] / src / leap / keymanager / __init__.py
1 # -*- coding: utf-8 -*-
2 # __init__.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 Key Manager is a Nicknym agent for LEAP client.
19 """
20 # let's do a little sanity check to see if we're using the wrong gnupg
21 import sys
22
23 try:
24     from gnupg.gnupg import GPGUtilities
25     assert(GPGUtilities)  # pyflakes happy
26     from gnupg import __version__ as _gnupg_version
27     from pkg_resources import parse_version
28     assert(parse_version(_gnupg_version) >= parse_version('1.2.3'))
29
30 except (ImportError, AssertionError):
31     print "*******"
32     print "Ooops! It looks like there is a conflict in the installed version "
33     print "of gnupg."
34     print
35     print "Disclaimer: Ideally, we would need to work a patch and propose the "
36     print "merge to upstream. But until then do: "
37     print
38     print "% pip uninstall python-gnupg"
39     print "% pip install gnupg"
40     print "*******"
41     sys.exit(1)
42
43 import logging
44 import requests
45
46 from twisted.internet import defer
47
48 from leap.common.check import leap_assert
49 from leap.common.events import signal
50 from leap.common.events import events_pb2 as proto
51 from leap.common.decorators import memoized_method
52
53 from leap.keymanager.errors import (
54     KeyNotFound,
55     KeyAddressMismatch,
56     KeyNotValidUpgrade,
57     UnsupportedKeyTypeError,
58     InvalidSignature
59 )
60 from leap.keymanager.validation import ValidationLevel, can_upgrade
61
62 from leap.keymanager.keys import (
63     build_key_from_dict,
64     KEYMANAGER_KEY_TAG,
65     TAGS_PRIVATE_INDEX,
66 )
67 from leap.keymanager.openpgp import (
68     OpenPGPKey,
69     OpenPGPScheme,
70 )
71
72 logger = logging.getLogger(__name__)
73
74
75 #
76 # The Key Manager
77 #
78
79 class KeyManager(object):
80
81     #
82     # server's key storage constants
83     #
84
85     OPENPGP_KEY = 'openpgp'
86     PUBKEY_KEY = "user[public_key]"
87
88     def __init__(self, address, nickserver_uri, soledad, token=None,
89                  ca_cert_path=None, api_uri=None, api_version=None, uid=None,
90                  gpgbinary=None):
91         """
92         Initialize a Key Manager for user's C{address} with provider's
93         nickserver reachable in C{nickserver_uri}.
94
95         :param address: The email address of the user of this Key Manager.
96         :type address: str
97         :param nickserver_uri: The URI of the nickserver.
98         :type nickserver_uri: str
99         :param soledad: A Soledad instance for local storage of keys.
100         :type soledad: leap.soledad.Soledad
101         :param token: The token for interacting with the webapp API.
102         :type token: str
103         :param ca_cert_path: The path to the CA certificate.
104         :type ca_cert_path: str
105         :param api_uri: The URI of the webapp API.
106         :type api_uri: str
107         :param api_version: The version of the webapp API.
108         :type api_version: str
109         :param uid: The user's UID.
110         :type uid: str
111         :param gpgbinary: Name for GnuPG binary executable.
112         :type gpgbinary: C{str}
113         """
114         self._address = address
115         self._nickserver_uri = nickserver_uri
116         self._soledad = soledad
117         self._token = token
118         self.ca_cert_path = ca_cert_path
119         self.api_uri = api_uri
120         self.api_version = api_version
121         self.uid = uid
122         # a dict to map key types to their handlers
123         self._wrapper_map = {
124             OpenPGPKey: OpenPGPScheme(soledad, gpgbinary=gpgbinary),
125             # other types of key will be added to this mapper.
126         }
127         # the following are used to perform https requests
128         self._fetcher = requests
129         self._session = self._fetcher.session()
130
131     #
132     # utilities
133     #
134
135     def _key_class_from_type(self, ktype):
136         """
137         Return key class from string representation of key type.
138         """
139         return filter(
140             lambda klass: klass.__name__ == ktype,
141             self._wrapper_map).pop()
142
143     def _get(self, uri, data=None):
144         """
145         Send a GET request to C{uri} containing C{data}.
146
147         :param uri: The URI of the request.
148         :type uri: str
149         :param data: The body of the request.
150         :type data: dict, str or file
151
152         :return: The response to the request.
153         :rtype: requests.Response
154         """
155         leap_assert(
156             self._ca_cert_path is not None,
157             'We need the CA certificate path!')
158         res = self._fetcher.get(uri, data=data, verify=self._ca_cert_path)
159         # Nickserver now returns 404 for key not found and 500 for
160         # other cases (like key too small), so we are skipping this
161         # check for the time being
162         # res.raise_for_status()
163
164         # Responses are now text/plain, although it's json anyway, but
165         # this will fail when it shouldn't
166         # leap_assert(
167         #     res.headers['content-type'].startswith('application/json'),
168         #     'Content-type is not JSON.')
169         return res
170
171     def _put(self, uri, data=None):
172         """
173         Send a PUT request to C{uri} containing C{data}.
174
175         The request will be sent using the configured CA certificate path to
176         verify the server certificate and the configured session id for
177         authentication.
178
179         :param uri: The URI of the request.
180         :type uri: str
181         :param data: The body of the request.
182         :type data: dict, str or file
183
184         :return: The response to the request.
185         :rtype: requests.Response
186         """
187         leap_assert(
188             self._ca_cert_path is not None,
189             'We need the CA certificate path!')
190         leap_assert(
191             self._token is not None,
192             'We need a token to interact with webapp!')
193         res = self._fetcher.put(
194             uri, data=data, verify=self._ca_cert_path,
195             headers={'Authorization': 'Token token=%s' % self._token})
196         # assert that the response is valid
197         res.raise_for_status()
198         return res
199
200     @memoized_method(invalidation=300)
201     def _fetch_keys_from_server(self, address):
202         """
203         Fetch keys bound to address from nickserver and insert them in
204         local database.
205
206         :param address: The address bound to the keys.
207         :type address: str
208
209         :return: A Deferred which fires when the key is in the storage,
210                  or which fails with KeyNotFound if the key was not found on
211                  nickserver.
212         :rtype: Deferred
213
214         """
215         # request keys from the nickserver
216         d = defer.succeed(None)
217         res = None
218         try:
219             res = self._get(self._nickserver_uri, {'address': address})
220             res.raise_for_status()
221             server_keys = res.json()
222             # insert keys in local database
223             if self.OPENPGP_KEY in server_keys:
224                 d = self.put_raw_key(
225                     server_keys['openpgp'],
226                     OpenPGPKey,
227                     address=address,
228                     validation=ValidationLevel.Provider_Trust)
229         except requests.exceptions.HTTPError as e:
230             if e.response.status_code == 404:
231                 d = defer.fail(KeyNotFound(address))
232             else:
233                 d = defer.fail(e)
234             logger.warning("HTTP error retrieving key: %r" % (e,))
235             logger.warning("%s" % (res.content,))
236         except Exception as e:
237             d = defer.fail(e)
238             logger.warning("Error retrieving key: %r" % (e,))
239         return d
240
241     #
242     # key management
243     #
244
245     def send_key(self, ktype):
246         """
247         Send user's key of type ktype to provider.
248
249         Public key bound to user's is sent to provider, which will sign it and
250         replace any prior keys for the same address in its database.
251
252         :param ktype: The type of the key.
253         :type ktype: subclass of EncryptionKey
254
255         :return: A Deferred which fires when the key is sent, or which fails
256                  with KeyNotFound if the key was not found in local database.
257         :rtype: Deferred
258
259         :raise UnsupportedKeyTypeError: if invalid key type
260         """
261         self._assert_supported_key_type(ktype)
262
263         def send(pubkey):
264             data = {
265                 self.PUBKEY_KEY: pubkey.key_data
266             }
267             uri = "%s/%s/users/%s.json" % (
268                 self._api_uri,
269                 self._api_version,
270                 self._uid)
271             self._put(uri, data)
272             signal(proto.KEYMANAGER_DONE_UPLOADING_KEYS, self._address)
273
274         d = self.get_key(
275             self._address, ktype, private=False, fetch_remote=False)
276         d.addCallback(send)
277         return d
278
279     def get_key(self, address, ktype, private=False, fetch_remote=True):
280         """
281         Return a key of type ktype bound to address.
282
283         First, search for the key in local storage. If it is not available,
284         then try to fetch from nickserver.
285
286         :param address: The address bound to the key.
287         :type address: str
288         :param ktype: The type of the key.
289         :type ktype: subclass of EncryptionKey
290         :param private: Look for a private key instead of a public one?
291         :type private: bool
292         :param fetch_remote: If key not found in local storage try to fetch
293                              from nickserver
294         :type fetch_remote: bool
295
296         :return: A Deferred which fires with an EncryptionKey of type ktype
297                  bound to address, or which fails with KeyNotFound if no key
298                  was found neither locally or in keyserver.
299         :rtype: Deferred
300
301         :raise UnsupportedKeyTypeError: if invalid key type
302         """
303         self._assert_supported_key_type(ktype)
304         logger.debug("getting key for %s" % (address,))
305         leap_assert(
306             ktype in self._wrapper_map,
307             'Unkown key type: %s.' % str(ktype))
308         signal(proto.KEYMANAGER_LOOKING_FOR_KEY, address)
309
310         def key_found(key):
311             signal(proto.KEYMANAGER_KEY_FOUND, address)
312             return key
313
314         def key_not_found(failure):
315             if not failure.check(KeyNotFound):
316                 return failure
317
318             signal(proto.KEYMANAGER_KEY_NOT_FOUND, address)
319
320             # we will only try to fetch a key from nickserver if fetch_remote
321             # is True and the key is not private.
322             if fetch_remote is False or private is True:
323                 return failure
324
325             signal(proto.KEYMANAGER_LOOKING_FOR_KEY, address)
326             d = self._fetch_keys_from_server(address)
327             d.addCallback(
328                 lambda _:
329                 self._wrapper_map[ktype].get_key(address, private=False))
330             d.addCallback(key_found)
331             return d
332
333         # return key if it exists in local database
334         d = self._wrapper_map[ktype].get_key(address, private=private)
335         d.addCallbacks(key_found, key_not_found)
336         return d
337
338     def get_all_keys(self, private=False):
339         """
340         Return all keys stored in local database.
341
342         :param private: Include private keys
343         :type private: bool
344
345         :return: A Deferred which fires with a list of all keys in local db.
346         :rtype: Deferred
347         """
348         def build_keys(docs):
349             return map(
350                 lambda doc: build_key_from_dict(
351                     self._key_class_from_type(doc.content['type']),
352                     doc.content),
353                 docs)
354
355         # XXX: there is no check that the soledad indexes are ready, as it
356         #      happens with EncryptionScheme.
357         #      The usecases right now are not problematic. This could be solve
358         #      adding a keytype to this funciont and moving the soledad request
359         #      to the EncryptionScheme.
360         d = self._soledad.get_from_index(
361             TAGS_PRIVATE_INDEX,
362             KEYMANAGER_KEY_TAG,
363             '1' if private else '0')
364         d.addCallback(build_keys)
365         return d
366
367     def gen_key(self, ktype):
368         """
369         Generate a key of type ktype bound to the user's address.
370
371         :param ktype: The type of the key.
372         :type ktype: subclass of EncryptionKey
373
374         :return: A Deferred which fires with the generated EncryptionKey.
375         :rtype: Deferred
376
377         :raise UnsupportedKeyTypeError: if invalid key type
378         """
379         self._assert_supported_key_type(ktype)
380
381         def signal_finished(key):
382             signal(proto.KEYMANAGER_FINISHED_KEY_GENERATION, self._address)
383             return key
384
385         signal(proto.KEYMANAGER_STARTED_KEY_GENERATION, self._address)
386         d = self._wrapper_map[ktype].gen_key(self._address)
387         d.addCallback(signal_finished)
388         return d
389
390     #
391     # Setters/getters
392     #
393
394     def _get_token(self):
395         return self._token
396
397     def _set_token(self, token):
398         self._token = token
399
400     token = property(
401         _get_token, _set_token, doc='The session token.')
402
403     def _get_ca_cert_path(self):
404         return self._ca_cert_path
405
406     def _set_ca_cert_path(self, ca_cert_path):
407         self._ca_cert_path = ca_cert_path
408
409     ca_cert_path = property(
410         _get_ca_cert_path, _set_ca_cert_path,
411         doc='The path to the CA certificate.')
412
413     def _get_api_uri(self):
414         return self._api_uri
415
416     def _set_api_uri(self, api_uri):
417         self._api_uri = api_uri
418
419     api_uri = property(
420         _get_api_uri, _set_api_uri, doc='The webapp API URI.')
421
422     def _get_api_version(self):
423         return self._api_version
424
425     def _set_api_version(self, api_version):
426         self._api_version = api_version
427
428     api_version = property(
429         _get_api_version, _set_api_version, doc='The webapp API version.')
430
431     def _get_uid(self):
432         return self._uid
433
434     def _set_uid(self, uid):
435         self._uid = uid
436
437     uid = property(
438         _get_uid, _set_uid, doc='The uid of the user.')
439
440     #
441     # encrypt/decrypt and sign/verify API
442     #
443
444     def encrypt(self, data, address, ktype, passphrase=None, sign=None,
445                 cipher_algo='AES256', fetch_remote=True):
446         """
447         Encrypt data with the public key bound to address and sign with with
448         the private key bound to sign address.
449
450         :param data: The data to be encrypted.
451         :type data: str
452         :param address: The address to encrypt it for.
453         :type address: str
454         :param ktype: The type of the key.
455         :type ktype: subclass of EncryptionKey
456         :param passphrase: The passphrase for the secret key used for the
457                            signature.
458         :type passphrase: str
459         :param sign: The address to be used for signature.
460         :type sign: str
461         :param cipher_algo: The cipher algorithm to use.
462         :type cipher_algo: str
463         :param fetch_remote: If key is not found in local storage try to fetch
464                              from nickserver
465         :type fetch_remote: bool
466
467         :return: A Deferred which fires with the encrypted data as str, or
468                  which fails with KeyNotFound if no keys were found neither
469                  locally or in keyserver or fails with EncryptError if failed
470                  encrypting for some reason.
471         :rtype: Deferred
472
473         :raise UnsupportedKeyTypeError: if invalid key type
474         """
475         self._assert_supported_key_type(ktype)
476
477         def encrypt(keys):
478             pubkey, signkey = keys
479             encrypted = self._wrapper_map[ktype].encrypt(
480                 data, pubkey, passphrase, sign=signkey,
481                 cipher_algo=cipher_algo)
482             pubkey.encr_used = True
483             d = self._wrapper_map[ktype].put_key(pubkey, address)
484             d.addCallback(lambda _: encrypted)
485             return d
486
487         dpub = self.get_key(address, ktype, private=False,
488                             fetch_remote=fetch_remote)
489         dpriv = defer.succeed(None)
490         if sign is not None:
491             dpriv = self.get_key(sign, ktype, private=True)
492         d = defer.gatherResults([dpub, dpriv], consumeErrors=True)
493         d.addCallbacks(encrypt, self._extract_first_error)
494         return d
495
496     def decrypt(self, data, address, ktype, passphrase=None, verify=None,
497                 fetch_remote=True):
498         """
499         Decrypt data using private key from address and verify with public key
500         bound to verify address.
501
502         :param data: The data to be decrypted.
503         :type data: str
504         :param address: The address to whom data was encrypted.
505         :type address: str
506         :param ktype: The type of the key.
507         :type ktype: subclass of EncryptionKey
508         :param passphrase: The passphrase for the secret key used for
509                            decryption.
510         :type passphrase: str
511         :param verify: The address to be used for signature.
512         :type verify: str
513         :param fetch_remote: If key for verify not found in local storage try
514                              to fetch from nickserver
515         :type fetch_remote: bool
516
517         :return: A Deferred which fires with:
518             * (decripted str, signing key) if validation works
519             * (decripted str, KeyNotFound) if signing key not found
520             * (decripted str, InvalidSignature) if signature is invalid
521             * KeyNotFound failure if private key not found
522             * DecryptError failure if decription failed
523         :rtype: Deferred
524
525         :raise UnsupportedKeyTypeError: if invalid key type
526         """
527         self._assert_supported_key_type(ktype)
528
529         def decrypt(keys):
530             pubkey, privkey = keys
531             decrypted, signed = self._wrapper_map[ktype].decrypt(
532                 data, privkey, passphrase=passphrase, verify=pubkey)
533             if pubkey is None:
534                 signature = KeyNotFound(verify)
535             elif signed:
536                 pubkey.sign_used = True
537                 d = self._wrapper_map[ktype].put_key(pubkey, address)
538                 d.addCallback(lambda _: (decrypted, pubkey))
539                 return d
540             else:
541                 signature = InvalidSignature(
542                     'Failed to verify signature with key %s' %
543                     (pubkey.key_id,))
544             return (decrypted, signature)
545
546         dpriv = self.get_key(address, ktype, private=True)
547         dpub = defer.succeed(None)
548         if verify is not None:
549             dpub = self.get_key(verify, ktype, private=False,
550                                 fetch_remote=fetch_remote)
551             dpub.addErrback(lambda f: None if f.check(KeyNotFound) else f)
552         d = defer.gatherResults([dpub, dpriv], consumeErrors=True)
553         d.addCallbacks(decrypt, self._extract_first_error)
554         return d
555
556     def _extract_first_error(self, failure):
557         return failure.value.subFailure
558
559     def sign(self, data, address, ktype, digest_algo='SHA512', clearsign=False,
560              detach=True, binary=False):
561         """
562         Sign data with private key bound to address.
563
564         :param data: The data to be signed.
565         :type data: str
566         :param address: The address to be used to sign.
567         :type address: EncryptionKey
568         :param ktype: The type of the key.
569         :type ktype: subclass of EncryptionKey
570         :param digest_algo: The hash digest to use.
571         :type digest_algo: str
572         :param clearsign: If True, create a cleartext signature.
573         :type clearsign: bool
574         :param detach: If True, create a detached signature.
575         :type detach: bool
576         :param binary: If True, do not ascii armour the output.
577         :type binary: bool
578
579         :return: A Deferred which fires with the signed data as str or fails
580                  with KeyNotFound if no key was found neither locally or in
581                  keyserver or fails with SignFailed if there was any error
582                  signing.
583         :rtype: Deferred
584
585         :raise UnsupportedKeyTypeError: if invalid key type
586         """
587         self._assert_supported_key_type(ktype)
588
589         def sign(privkey):
590             return self._wrapper_map[ktype].sign(
591                 data, privkey, digest_algo=digest_algo, clearsign=clearsign,
592                 detach=detach, binary=binary)
593
594         d = self.get_key(address, ktype, private=True)
595         d.addCallback(sign)
596         return d
597
598     def verify(self, data, address, ktype, detached_sig=None,
599                fetch_remote=True):
600         """
601         Verify signed data with private key bound to address, eventually using
602         detached_sig.
603
604         :param data: The data to be verified.
605         :type data: str
606         :param address: The address to be used to verify.
607         :type address: EncryptionKey
608         :param ktype: The type of the key.
609         :type ktype: subclass of EncryptionKey
610         :param detached_sig: A detached signature. If given, C{data} is
611                              verified using this detached signature.
612         :type detached_sig: str
613         :param fetch_remote: If key for verify not found in local storage try
614                              to fetch from nickserver
615         :type fetch_remote: bool
616
617         :return: A Deferred which fires with the signing EncryptionKey if
618                  signature verifies, or which fails with InvalidSignature if
619                  signature don't verifies or fails with KeyNotFound if no key
620                  was found neither locally or in keyserver.
621         :rtype: Deferred
622
623         :raise UnsupportedKeyTypeError: if invalid key type
624         """
625         self._assert_supported_key_type(ktype)
626
627         def verify(pubkey):
628             signed = self._wrapper_map[ktype].verify(
629                 data, pubkey, detached_sig=detached_sig)
630             if signed:
631                 pubkey.sign_used = True
632                 d = self._wrapper_map[ktype].put_key(pubkey, address)
633                 d.addCallback(lambda _: pubkey)
634                 return d
635             else:
636                 raise InvalidSignature(
637                     'Failed to verify signature with key %s' %
638                     (pubkey.key_id,))
639
640         d = self.get_key(address, ktype, private=False,
641                          fetch_remote=fetch_remote)
642         d.addCallback(verify)
643         return d
644
645     def delete_key(self, key):
646         """
647         Remove key from storage.
648
649         :param key: The key to be removed.
650         :type key: EncryptionKey
651
652         :return: A Deferred which fires when the key is deleted, or which fails
653                  KeyNotFound if the key was not found on local storage.
654         :rtype: Deferred
655
656         :raise UnsupportedKeyTypeError: if invalid key type
657         """
658         self._assert_supported_key_type(type(key))
659         return self._wrapper_map[type(key)].delete_key(key)
660
661     def put_key(self, key, address):
662         """
663         Put key bound to address in local storage.
664
665         :param key: The key to be stored
666         :type key: EncryptionKey
667         :param address: address for which this key will be active
668         :type address: str
669
670         :return: A Deferred which fires when the key is in the storage, or
671                  which fails with KeyAddressMismatch if address doesn't match
672                  any uid on the key or fails with KeyNotValidUpdate if a key
673                  with the same uid exists and the new one is not a valid update
674                  for it.
675         :rtype: Deferred
676
677         :raise UnsupportedKeyTypeError: if invalid key type
678         """
679         self._assert_supported_key_type(type(key))
680
681         if address not in key.address:
682             return defer.fail(
683                 KeyAddressMismatch("UID %s found, but expected %s"
684                                    % (str(key.address), address)))
685
686         def old_key_not_found(failure):
687             if failure.check(KeyNotFound):
688                 return None
689             else:
690                 return failure
691
692         def check_upgrade(old_key):
693             if key.private or can_upgrade(key, old_key):
694                 return self._wrapper_map[type(key)].put_key(key, address)
695             else:
696                 raise KeyNotValidUpgrade(
697                     "Key %s can not be upgraded by new key %s"
698                     % (old_key.key_id, key.key_id))
699
700         d = self._wrapper_map[type(key)].get_key(address,
701                                                  private=key.private)
702         d.addErrback(old_key_not_found)
703         d.addCallback(check_upgrade)
704         return d
705
706     def put_raw_key(self, key, ktype, address,
707                     validation=ValidationLevel.Weak_Chain):
708         """
709         Put raw key bound to address in local storage.
710
711         :param key: The ascii key to be stored
712         :type key: str
713         :param ktype: the type of the key.
714         :type ktype: subclass of EncryptionKey
715         :param address: address for which this key will be active
716         :type address: str
717         :param validation: validation level for this key
718                            (default: 'Weak_Chain')
719         :type validation: ValidationLevel
720
721         :return: A Deferred which fires when the key is in the storage, or
722                  which fails with KeyAddressMismatch if address doesn't match
723                  any uid on the key or fails with KeyNotValidUpdate if a key
724                  with the same uid exists and the new one is not a valid update
725                  for it.
726         :rtype: Deferred
727
728         :raise UnsupportedKeyTypeError: if invalid key type
729         """
730         self._assert_supported_key_type(ktype)
731         pubkey, privkey = self._wrapper_map[ktype].parse_ascii_key(key)
732         pubkey.validation = validation
733         d = self.put_key(pubkey, address)
734         if privkey is not None:
735             d.addCallback(lambda _: self.put_key(privkey, address))
736         return d
737
738     def fetch_key(self, address, uri, ktype,
739                   validation=ValidationLevel.Weak_Chain):
740         """
741         Fetch a public key bound to address from the network and put it in
742         local storage.
743
744         :param address: The email address of the key.
745         :type address: str
746         :param uri: The URI of the key.
747         :type uri: str
748         :param ktype: the type of the key.
749         :type ktype: subclass of EncryptionKey
750         :param validation: validation level for this key
751                            (default: 'Weak_Chain')
752         :type validation: ValidationLevel
753
754         :return: A Deferred which fires when the key is in the storage, or
755                  which fails with KeyNotFound: if not valid key on uri or fails
756                  with KeyAddressMismatch if address doesn't match any uid on
757                  the key or fails with KeyNotValidUpdate if a key with the same
758                  uid exists and the new one is not a valid update for it.
759         :rtype: Deferred
760
761         :raise UnsupportedKeyTypeError: if invalid key type
762         """
763         self._assert_supported_key_type(ktype)
764
765         res = self._get(uri)
766         if not res.ok:
767             return defer.fail(KeyNotFound(uri))
768
769         # XXX parse binary keys
770         pubkey, _ = self._wrapper_map[ktype].parse_ascii_key(res.content)
771         if pubkey is None:
772             return defer.fail(KeyNotFound(uri))
773
774         pubkey.validation = validation
775         return self.put_key(pubkey, address)
776
777     def _assert_supported_key_type(self, ktype):
778         """
779         Check if ktype is one of the supported key types
780
781         :param ktype: the type of the key.
782         :type ktype: subclass of EncryptionKey
783
784         :raise UnsupportedKeyTypeError: if invalid key type
785         """
786         if ktype not in self._wrapper_map:
787             raise UnsupportedKeyTypeError(str(ktype))
788
789 from ._version import get_versions
790 __version__ = get_versions()['version']
791 del get_versions