def import_key(email, encryption_name, public_key, id_fingerprint_pairs, plugin): ''' Import the key and return the fingerprint. ''' status = fingerprint = None result_ok = False # make sure the email address is in the key for (user_id, fingerprint) in id_fingerprint_pairs: user_name, email_address = parse_address(email) key_address = get_email(user_id) if email_address.lower() == key_address.lower(): result_ok = True break if result_ok: status = '' for (user_id, fingerprint) in id_fingerprint_pairs: result_ok = plugin.import_public(public_key, id_fingerprint_pairs) if result_ok: status += '{}\n'.format( i18n( 'Imported key successfully. Fingerprint: {fingerprint}' .format(fingerprint=fingerprint))) else: status += '{}\n'.format(i18n('Unable to import key')) else: status = i18n( "Cannot import the key because it isn't for {email}".format( email=email)) return result_ok, status
def clean(self): '''Verify there is only 1 general info record.''' error_message = None cleaned_data = super(ImportKeyFromKeyserverForm, self).clean() email_or_fingerprint = cleaned_data.get('email_or_fingerprint') __, email = parse_address(email_or_fingerprint) if email is None: fingerprint = strip_fingerprint(email_or_fingerprint) m = re.match('^[0-9A-Fa-f]+$', fingerprint) if m: if len(fingerprint) < 16: error_message = i18n( 'Either enter a valid email address or a fingerprint that is least 16 characters' ) else: error_message = i18n( 'Either enter a valid email address or a fingerprint which must contain only numbers and the letters A through F.' ) if error_message is not None: _log.write(error_message) raise forms.ValidationError(error_message, code='invalid') return cleaned_data
def import_key(email, encryption_name, public_key, id_fingerprint_pairs, plugin): ''' Import the key and return the fingerprint. ''' status = fingerprint = None result_ok = False # make sure the email address is in the key for (user_id, fingerprint) in id_fingerprint_pairs: user_name, email_address = parse_address(email) key_address = get_email(user_id) if email_address.lower() == key_address.lower(): result_ok = True break if result_ok: status = '' for (user_id, fingerprint) in id_fingerprint_pairs: result_ok = plugin.import_public(public_key, id_fingerprint_pairs) if result_ok: status += '{}\n'.format(i18n('Imported key successfully. Fingerprint: {fingerprint}'.format( fingerprint=fingerprint))) else: status += '{}\n'.format(i18n('Unable to import key')) else: status = i18n("Cannot import the key because it isn't for {email}".format(email=email)) return result_ok, status
def manage_keys_in_header(self, crypto_message): ''' Manage all the public keys in the message's header. ''' header_contains_key_info = False try: from_user = crypto_message.smtp_sender() self.recipient_to_notify = crypto_message.smtp_recipient() # all notices about a metadata address goes to the admin if is_metadata_address(self.recipient_to_notify): self.recipient_to_notify = get_admin_email() name, address = parse_address(from_user) if address is None or crypto_message is None or crypto_message.get_email_message() is None: self.log_message('missing data so cannot import key') self.log_message(' from user: {}'.format(from_user)) self.log_message(' address: {}'.format(address)) self.log_message(' crypto message: {}'.format(crypto_message)) if crypto_message is not None: self.log_message(' email message: {}'.format(crypto_message.get_email_message())) else: accepted_crypto_packages = crypto_message.get_accepted_crypto_software() if accepted_crypto_packages is None or len(accepted_crypto_packages) <= 0: self.log_message("checking for default key for {} <{}>".format(name, address)) tag = self._manage_key_header(address, crypto_message, KeyFactory.get_default_encryption_name(), PUBLIC_KEY_HEADER) else: self.log_message("checking for {} keys".format(accepted_crypto_packages)) for encryption_name in accepted_crypto_packages: # see if there's a the key block for this encryption program header_name = get_public_key_header_name(encryption_name) key_block = get_multientry_header( crypto_message.get_email_message().get_message(), header_name) # see if there's a plain key block if ((key_block is None or len(key_block) <= 0) and len(accepted_crypto_packages) == 1): self.log_message("no {} public key in header so trying generic header".format(encryption_name)) key_block = get_multientry_header( crypto_message.get_email_message().get_message(), PUBLIC_KEY_HEADER) tag = self._manage_key_header( address, crypto_message, encryption_name, key_block) header_contains_key_info = True self.update_accepted_crypto(from_user, accepted_crypto_packages) except MessageException as message_exception: self.log_message(message_exception.value) raise MessageException(value=message_exception.value) except: record_exception() self.log_message('EXCEPTION - see syr.exception.log for details') if crypto_message is not None: crypto_message.add_error_tag_once(self.UNEXPECTED_ERROR) self.log_message('header_contains_key_info: {}'.format(header_contains_key_info)) return header_contains_key_info
def import_key_from_keyserver(request, form): ''' Import a key from a keyserver, if found. ''' MISSING_USER_EMAIL_ERROR = i18n( 'Unable to search for keys. Searches can take a long time so when the search finds a key, you would receive an email message.<p>Your User account does not have an email address defined so searches cannot be performed. Contact your mail administrator and request they enter your email address in your User account.') UNEXPECTED_START_SEARCH_ERROR = i18n('An unexpected error was detected when starting search for the key. Contact your mail administrator.') NO_ACTIVE_KEYSERVERS_ERROR = i18n('Unable to search for key because there are no active keyservers. Contact your mail administrator.') UNEXPECTED_SEARCH_ERROR = i18n('Unexpected error while starting search for key. Contact your mail administrator.') response = status = None try: log_message('searching for key on keyservers and importing if found') email_or_fingerprint = form.cleaned_data['email_or_fingerprint'] encryption_software = form.cleaned_data['encryption_software'] user_requesting_search = request.user.email if user_requesting_search is None or len(user_requesting_search) <= 0: status = MISSING_USER_EMAIL_ERROR else: max_wait_time = len(get_active_keyservers(KeyFactory.DEFAULT_ENCRYPTION_NAME)) * 5 * 60 if max_wait_time > 0: __, email = parse_address(email_or_fingerprint) if email is None: fingerprint = email_or_fingerprint result_ok = queue_keyserver_retrieval( fingerprint, encryption_software.name, user_requesting_search) log_message('started retrieving key using {} key id'.format(fingerprint)) else: result_ok = queue_keyserver_search( email, user_requesting_search, interactive=True) log_message('started searching for key for {}'.format(email)) if result_ok: page_title = i18n("Starting search for Key") data = {'page_title': page_title, 'status': 'Searches can take a long time. You will receive email with the results.'} template = 'mail/import_from_keyserver_results.html' else: status = UNEXPECTED_START_SEARCH_ERROR else: status = NO_ACTIVE_KEYSERVERS_ERROR except: status = UNEXPECTED_SEARCH_ERROR record_exception() if status is not None: page_title = i18n("Import Key From Keyserver Error") data = {'page_title': page_title, 'result': status} template = 'mail/key_error_results.html' log_message(status) response = render_to_response(template, data, context_instance=RequestContext(request)) return response
def make_key(line, domain, crypto_name): ''' Make a private key if its in the domain.''' user_name, email = parse_address(line) if email is None: print('{} is not a valid email address'.format(line)) elif email_in_domain(email): if user_name is None or len(user_name.strip()) <= 0: full_address = email else: full_address = '{} <{}>'.format(user_name, email) contacts.add(full_address, crypto_name, source=AUTO_GENERATED) else: print('{} not in the domain: {}'.format(line, domain))
def delete(email_or_address): ''' Delete the contact with a matching email address. >>> # In honor of Amy Goodman, who hosts Democracy Now! # returns true because the address doesn't exist, even though it wasn't necessary to delete it. >>> delete('*****@*****.**') True # test the extreme cases >>> delete(None) False ''' result_ok = True try: if is_string(email_or_address): name, address = parse_address(email_or_address) contact = get(address) if contact is None: log_message('no {} <{}> contact to delete'.format( name, address)) else: contact.delete() log_message("deleted {}".format(contact.email)) elif email_or_address is None: result_ok = False elif isinstance(email_or_address, Contact): contact = email_or_address contact.delete() log_message("deleted {}".format(contact.email)) else: result_ok = False log_message( "unable to delete contact because wront type: {}".format( type(email_or_address))) except Exception: result_ok = False record_exception() log_message('EXCEPTION - see syr.exception.log for details') return result_ok
def exists(email): ''' Determine if the contact exists already. Test an unknown email address so we're sure of the result. See the unittest to understand how to really use this function. >>> # In honor of Thomas Tamm, who was a whistleblower to the NY Times about senior >>> # Justice officials fight against the widening scope of warrantless NSA surveillance. >>> exists('*****@*****.**') False ''' name, address = parse_address(email) contact = get(address) found = contact is not None and contact.email == address log_message("{} <{}> contact exists: {}".format(name, address, found)) return found
def delete(email_or_address): ''' Delete the contact with a matching email address. >>> # In honor of Amy Goodman, who hosts Democracy Now! # returns true because the address doesn't exist, even though it wasn't necessary to delete it. >>> delete('*****@*****.**') True # test the extreme cases >>> delete(None) False ''' result_ok = True try: if is_string(email_or_address): name, address = parse_address(email_or_address) contact = get(address) if contact is None: log_message('no {} <{}> contact to delete'.format(name, address)) else: contact.delete() log_message("deleted {}".format(contact.email)) elif email_or_address is None: result_ok = False elif isinstance(email_or_address, Contact): contact = email_or_address contact.delete() log_message("deleted {}".format(contact.email)) else: result_ok = False log_message("unable to delete contact because wront type: {}".format(type(email_or_address))) except Exception: result_ok = False record_exception() log_message('EXCEPTION - see syr.exception.log for details') return result_ok
def manage_keys_in_header(self, crypto_message): ''' Manage all the public keys in the message's header. ''' header_contains_key_info = False try: from_user = crypto_message.smtp_sender() self.recipient_to_notify = crypto_message.smtp_recipient() # all notices about a metadata address goes to the admin if is_metadata_address(self.recipient_to_notify): self.recipient_to_notify = get_admin_email() name, address = parse_address(from_user) if address is None or crypto_message is None or crypto_message.get_email_message( ) is None: self.log_message('missing data so cannot import key') self.log_message(' from user: {}'.format(from_user)) self.log_message(' address: {}'.format(address)) self.log_message( ' crypto message: {}'.format(crypto_message)) if crypto_message is not None: self.log_message(' email message: {}'.format( crypto_message.get_email_message())) else: accepted_crypto_packages = crypto_message.get_accepted_crypto_software( ) if accepted_crypto_packages is None or len( accepted_crypto_packages) <= 0: self.log_message( "checking for default key for {} <{}>".format( name, address)) tag = self._manage_key_header( address, crypto_message, KeyFactory.get_default_encryption_name(), PUBLIC_KEY_HEADER) else: self.log_message("checking for {} keys".format( accepted_crypto_packages)) for encryption_name in accepted_crypto_packages: # see if there's a the key block for this encryption program header_name = get_public_key_header_name( encryption_name) key_block = get_multientry_header( crypto_message.get_email_message().get_message(), header_name) # see if there's a plain key block if ((key_block is None or len(key_block) <= 0) and len(accepted_crypto_packages) == 1): self.log_message( "no {} public key in header so trying generic header" .format(encryption_name)) key_block = get_multientry_header( crypto_message.get_email_message().get_message( ), PUBLIC_KEY_HEADER) tag = self._manage_key_header(address, crypto_message, encryption_name, key_block) header_contains_key_info = True self.update_accepted_crypto(from_user, accepted_crypto_packages) except MessageException as message_exception: self.log_message(message_exception.value) raise MessageException(value=message_exception.value) except: record_exception() self.log_message('EXCEPTION - see syr.exception.log for details') if crypto_message is not None: crypto_message.add_error_tag_once(self.UNEXPECTED_ERROR) self.log_message( 'header_contains_key_info: {}'.format(header_contains_key_info)) return header_contains_key_info
def add_contact_records(email_or_fingerprint, crypto_name, user_initiated_search, job_id, queue_key): ''' Add contact and associated crypto records in database. This function cannot be part of a class because it's passed to RQ which only accepts standalone functions, not functions in an instance of a class. Also, the function must be defined before the class which calls it or located in a separate file and imported. Test extreme case. >>> add_contact_records(None, None, None, None, None) False ''' result_ok = timed_out = False log_message('entered add_contact_records') try: __, email = parse_address(email_or_fingerprint) if email is None: key_id = email_or_fingerprint else: key_id = None log_message('adding a {} contact for {} if key retrieved by {} job'.format( crypto_name, email_or_fingerprint, job_id)) key_plugin = KeyFactory.get_crypto( crypto_name, crypto_software.get_key_classname(crypto_name)) if queue_key is None or job_id is None: result_ok = False else: MAX_WAIT = 10 * 60 # seconds queue = Queue.from_queue_key(queue_key, connection=key_plugin.get_queue_connection()) job = queue.fetch_job(job_id) # if this function is added to a queue (RQ), then it never starts # so we're going to do this the old fashion way waited = 0 while not job.is_finished and not job.is_failed and waited < MAX_WAIT: sleep(1) if job.is_failed: log_message('retrieving {} key for {} job failed'.format(crypto_name, email_or_fingerprint)) result_ok = False else: # even if the job timed out, see if the key was retrieved result_ok, timed_out, output, error = key_plugin.get_background_job_results( email_or_fingerprint, job) log_message("results from retrieving {} key for {}, result ok: {}; timed out: {}".format( crypto_name, email_or_fingerprint, result_ok, timed_out)) if result_ok: imported_user_ids = key_plugin.parse_keyserver_ids_retrieved(error) log_message("imported user ids: {}".format(imported_user_ids)) if len(imported_user_ids) < 1 and error: log_message('error: {}'.format(error)) if output: log_message('output: {}'.format(output)) if result_ok: id_fingerprint_pairs = [] for user_id, imported_key_id in imported_user_ids: contact = contacts.add(user_id, crypto_name, source=KEYSERVER) result_ok = contact is not None log_message("added contact's crypto for {}: {}".format(user_id, result_ok)) if result_ok: # change the outgoing policy if needed if contact.outbound_encrypt_policy != DEFAULT_OUTBOUND_ENCRYPT_POLICY: contact.outbound_encrypt_policy = DEFAULT_OUTBOUND_ENCRYPT_POLICY contact.save() # activate the contact's crypto "after save signal" to update the fingerprint contacts_crypto = contacts.get_contacts_crypto(user_id, crypto_name) if contacts_crypto is not None: if contacts_crypto.fingerprint is None: contacts_crypto.source = KEYSERVER contacts_crypto.save() if key_id is None: fingerprint = imported_key_id else: fingerprint = key_id else: fingerprint = contacts_crypto.fingerprint elif key_id is None: fingerprint = imported_key_id else: fingerprint = key_id id_fingerprint_pairs.append((contact.email, format_fingerprint(fingerprint))) else: log_message('unable to add {} contact record for {}'.format(crypto_name, user_id)) if result_ok and len(id_fingerprint_pairs) > 0: log_message('notifying {} about new keys: {}'.format(user_initiated_search, id_fingerprint_pairs)) notify_new_key_arrived(user_initiated_search, id_fingerprint_pairs) elif timed_out: log_message('timed out retrieving a {} key for {}.'.format( crypto_name, email_or_fingerprint)) else: log_message('unable to retrieve {} key for {}.'.format( crypto_name, email_or_fingerprint)) except Exception as exception: record_exception() log_message('EXCEPTION - see syr.exception.log for details') result_ok = False log_message('ended add_contact_records: {}'.format(result_ok)) return result_ok
def import_key_from_keyserver(request, form): ''' Import a key from a keyserver, if found. ''' MISSING_USER_EMAIL_ERROR = i18n( 'Unable to search for keys. Searches can take a long time so when the search finds a key, you would receive an email message.<p>Your User account does not have an email address defined so searches cannot be performed. Contact your mail administrator and request they enter your email address in your User account.' ) UNEXPECTED_START_SEARCH_ERROR = i18n( 'An unexpected error was detected when starting search for the key. Contact your mail administrator.' ) NO_ACTIVE_KEYSERVERS_ERROR = i18n( 'Unable to search for key because there are no active keyservers. Contact your mail administrator.' ) UNEXPECTED_SEARCH_ERROR = i18n( 'Unexpected error while starting search for key. Contact your mail administrator.' ) response = status = None try: log_message('searching for key on keyservers and importing if found') email_or_fingerprint = form.cleaned_data['email_or_fingerprint'] encryption_software = form.cleaned_data['encryption_software'] user_requesting_search = request.user.email if user_requesting_search is None or len(user_requesting_search) <= 0: status = MISSING_USER_EMAIL_ERROR else: max_wait_time = len( get_active_keyservers( KeyFactory.DEFAULT_ENCRYPTION_NAME)) * 5 * 60 if max_wait_time > 0: __, email = parse_address(email_or_fingerprint) if email is None: fingerprint = email_or_fingerprint result_ok = queue_keyserver_retrieval( fingerprint, encryption_software.name, user_requesting_search) log_message( 'started retrieving key using {} key id'.format( fingerprint)) else: result_ok = queue_keyserver_search(email, user_requesting_search, interactive=True) log_message( 'started searching for key for {}'.format(email)) if result_ok: page_title = i18n("Starting search for Key") data = { 'page_title': page_title, 'status': 'Searches can take a long time. You will receive email with the results.' } template = 'mail/import_from_keyserver_results.html' else: status = UNEXPECTED_START_SEARCH_ERROR else: status = NO_ACTIVE_KEYSERVERS_ERROR except: status = UNEXPECTED_SEARCH_ERROR record_exception() if status is not None: page_title = i18n("Import Key From Keyserver Error") data = {'page_title': page_title, 'result': status} template = 'mail/key_error_results.html' log_message(status) response = render_to_response(template, data, context_instance=RequestContext(request)) return response
def add_contact_records(email_or_fingerprint, crypto_name, user_initiated_search, job_id, queue_key): ''' Add contact and associated crypto records in database. This function cannot be part of a class because it's passed to RQ which only accepts standalone functions, not functions in an instance of a class. Also, the function must be defined before the class which calls it or located in a separate file and imported. Test extreme case. >>> add_contact_records(None, None, None, None, None) False ''' result_ok = timed_out = False log_message('entered add_contact_records') try: __, email = parse_address(email_or_fingerprint) if email is None: key_id = email_or_fingerprint else: key_id = None log_message( 'adding a {} contact for {} if key retrieved by {} job'.format( crypto_name, email_or_fingerprint, job_id)) key_plugin = KeyFactory.get_crypto( crypto_name, crypto_software.get_key_classname(crypto_name)) if queue_key is None or job_id is None: result_ok = False else: MAX_WAIT = 10 * 60 # seconds queue = Queue.from_queue_key( queue_key, connection=key_plugin.get_queue_connection()) job = queue.fetch_job(job_id) # if this function is added to a queue (RQ), then it never starts # so we're going to do this the old fashion way waited = 0 while not job.is_finished and not job.is_failed and waited < MAX_WAIT: sleep(1) if job.is_failed: log_message('retrieving {} key for {} job failed'.format( crypto_name, email_or_fingerprint)) result_ok = False else: # even if the job timed out, see if the key was retrieved result_ok, timed_out, output, error = key_plugin.get_background_job_results( email_or_fingerprint, job) log_message( "results from retrieving {} key for {}, result ok: {}; timed out: {}" .format(crypto_name, email_or_fingerprint, result_ok, timed_out)) if result_ok: imported_user_ids = key_plugin.parse_keyserver_ids_retrieved( error) log_message( "imported user ids: {}".format(imported_user_ids)) if len(imported_user_ids) < 1 and error: log_message('error: {}'.format(error)) if output: log_message('output: {}'.format(output)) if result_ok: id_fingerprint_pairs = [] for user_id, imported_key_id in imported_user_ids: contact = contacts.add(user_id, crypto_name, source=KEYSERVER) result_ok = contact is not None log_message("added contact's crypto for {}: {}".format( user_id, result_ok)) if result_ok: # change the outgoing policy if needed if contact.outbound_encrypt_policy != DEFAULT_OUTBOUND_ENCRYPT_POLICY: contact.outbound_encrypt_policy = DEFAULT_OUTBOUND_ENCRYPT_POLICY contact.save() # activate the contact's crypto "after save signal" to update the fingerprint contacts_crypto = contacts.get_contacts_crypto( user_id, crypto_name) if contacts_crypto is not None: if contacts_crypto.fingerprint is None: contacts_crypto.source = KEYSERVER contacts_crypto.save() if key_id is None: fingerprint = imported_key_id else: fingerprint = key_id else: fingerprint = contacts_crypto.fingerprint elif key_id is None: fingerprint = imported_key_id else: fingerprint = key_id id_fingerprint_pairs.append( (contact.email, format_fingerprint(fingerprint))) else: log_message( 'unable to add {} contact record for {}'.format( crypto_name, user_id)) if result_ok and len(id_fingerprint_pairs) > 0: log_message('notifying {} about new keys: {}'.format( user_initiated_search, id_fingerprint_pairs)) notify_new_key_arrived(user_initiated_search, id_fingerprint_pairs) elif timed_out: log_message('timed out retrieving a {} key for {}.'.format( crypto_name, email_or_fingerprint)) else: log_message('unable to retrieve {} key for {}.'.format( crypto_name, email_or_fingerprint)) except Exception as exception: record_exception() log_message('EXCEPTION - see syr.exception.log for details') result_ok = False log_message('ended add_contact_records: {}'.format(result_ok)) return result_ok
def add(email, encryption_program, fingerprint=None, passcode=None, source=None): ''' Add a contact and related settings. >>> # In honor of Thomas Drake, a whistleblower about Trailblazer, a NSA mass surveillance project. >>> email = '*****@*****.**' >>> encryption_software = crypto_software.get(KeyFactory.DEFAULT_ENCRYPTION_NAME) >>> contact = add(email, KeyFactory.DEFAULT_ENCRYPTION_NAME) >>> contact.email '*****@*****.**' >>> contact.user_name 'Thomas' >>> address = contact.email >>> address = '*****@*****.**' >>> contacts_crypto = ContactsCrypto.objects.get( ... contact=contact, encryption_software=encryption_software) >>> contacts_crypto is not None True >>> x = contact.delete() >>> contact = add(None, encryption_software) >>> contact is None True >>> contact = add(email, None) >>> contact.email = '*****@*****.**' >>> contact.user_name = 'Thomas' >>> get_contacts_crypto(email) [] >>> x = contact.delete() >>> contact = add(None, None) >>> contact is None True >>> contact = add('*****@*****.**', None) >>> contact.email = '*****@*****.**' >>> contact.user_name = 'test.com domain key (system use only)' >>> x = contact.delete() ''' try: new_contact = True user_name, email_address = parse_address(email) if email_address is None: contact = None else: try: contact = Contact.objects.get(email=email_address) new_contact = False # update the user name if it's been given and it differs from the name in the DB if user_name is not None and contact.user_name != user_name: contact.user_name = user_name contact.save() log_message('updated {} user name to {}'.format(email_address, user_name)) except Contact.DoesNotExist: log_message('creating a contact for {}'.format(email_address)) try: if user_name is None or len(user_name.strip()) <= 0: from goodcrypto.mail.message.metadata import is_metadata_address user_name = email_address i = user_name.find('@') # handle domain keys specially if is_metadata_address(email_address): if i > 0: email_domain = user_name[i+1:] user_name = '{} domain key (system use only)'.format(email_domain) else: if i > 0: user_name = user_name[:i] user_name = user_name.replace('.', ' ').replace('-', ' ').replace('_', ' ') user_name = capwords(user_name) except: pass contact = Contact.objects.create(email=email_address, user_name=user_name) except Exception: record_exception() log_message('EXCEPTION - see syr.exception.log for details') contact = None if encryption_program is None: log_message("no encryption software defined so not creating contact's crytpo record for {}".format(email)) else: # add a corresponding record for the contact's crypto program encryption_software = crypto_software.get(encryption_program) if contact is None or encryption_software is None: log_message('no contact and/or encryption software defined') else: try: contacts_crypto = ContactsCrypto.objects.get( contact=contact, encryption_software=encryption_software) if (fingerprint is not None and strip_fingerprint(contacts_crypto.fingerprint) != strip_fingerprint(fingerprint)): contacts_crypto.fingerprint = format_fingerprint(fingerprint) if email_in_domain(email): if contacts.crypto.source is None: contacts.crypto.source = constants.AUTO_GENERATED if contacts_crypto.source == constants.AUTO_GENERATED: contacts_crypto.verified = True contacts_crypto.save() except ContactsCrypto.DoesNotExist: # if the contact existed without any contact crypto, but was set # to never encrypt and now we have a key, then change the # outbound encrypt policy to the default if (not new_contact and contact.outbound_encrypt_policy == constants.NEVER_ENCRYPT_OUTBOUND): contact.outbound_encrypt_policy = constants.DEFAULT_OUTBOUND_ENCRYPT_POLICY contact.save() contacts_crypto = add_contacts_crypto(contact, encryption_software, fingerprint=fingerprint, source=source) log_message("created {} crypto record for {} with {} fingerprint: {}".format( encryption_software, email, fingerprint, contacts_crypto is not None)) except: record_exception() log_message('EXCEPTION - see syr.exception.log for details') except Exception: contact = None record_exception() log_message('EXCEPTION - see syr.exception.log for details') return contact
def create(self, user_id, passcode, expiration=None, wait_for_results=False): ''' Create a new key. If wait_for_results is False, then start the process, but don't wait for the results. If the key already exists and hasn't expired, then return True without creating a new key. If key generated while waiting or key generation started successfully when not waiting, then return True; otherwise, False. >>> # In honor of Moritz Bartl, advocate for the Tor project. >>> from goodcrypto.oce.key.key_factory import KeyFactory >>> plugin = KeyFactory.get_crypto(gpg_key_constants.NAME) >>> plugin.create('*****@*****.**', 'a secret code') (True, False, None, False) >>> # In honor of Roger Dingledine, one of the original developers of the Tor project. >>> from goodcrypto.oce.key.constants import EXPIRES_IN, EXPIRATION_UNIT >>> from goodcrypto.oce.key.key_factory import KeyFactory >>> email = '*****@*****.**' >>> plugin = KeyFactory.get_crypto(gpg_key_constants.NAME) >>> plugin.create(email, 'a secret code', wait_for_results=True) (True, False, None, False) >>> while not plugin.private_key_exists(email): ... sleep(10) >>> plugin.private_key_exists(email) True >>> encrypted_data, __ = plugin.encrypt_and_armor('Test data', email) >>> unencrypted_data, signed_by, result_code = plugin.decrypt(encrypted_data, 'a secret code') >>> result_code == gpg_constants.GOOD_RESULT True >>> unencrypted_data, signed_by, result_code = plugin.decrypt(encrypted_data, 'another code') >>> result_code == gpg_constants.ERROR_RESULT True >>> plugin.create(email, 'another code', wait_for_results=True) (True, False, None, True) >>> plugin.delete(email) True ''' result_code = gpg_constants.ERROR_RESULT result_ok = timed_out = key_already_exists = False fingerprint = None try: self.log_message('gen key for {} that expires within {}'.format(user_id, expiration)) name, email = parse_address(user_id) # gpg requires "real names" be at least 5 characters long if name == None or len(name) <= 4: index = email.find('@') if index > 0: name = email[:index].capitalize() else: name = email if len(name) <= 4: name = email expires_in, expiration_unit = gpg_utils.get_standardized_expiration(expiration) data = '' data += '{}{}{}'.format(gpg_constants.KEY_TYPE, self.DefaultKeyType, gpg_constants.EOL) data += '{}{}{}'.format(gpg_constants.KEY_LENGTH, self.DefaultKeyLength, gpg_constants.EOL) data += '{}{}{}'.format(gpg_constants.SUBKEY_TYPE, self.DefaultSubkeyType, gpg_constants.EOL) data += '{}{}{}'.format(gpg_constants.SUBKEY_LENGTH, self.DefaultKeyLength, gpg_constants.EOL) data += '{}{}{}{}'.format(gpg_constants.EXPIRE_DATE, expires_in, expiration_unit, gpg_constants.EOL) data += '{}{}{}'.format(gpg_constants.KEY_PASSPHRASE, passcode, gpg_constants.EOL) data += '{}{}{}'.format(gpg_constants.NAME_REAL, name, gpg_constants.EOL) data += '{}{}{}'.format(gpg_constants.NAME_EMAIL, email, gpg_constants.EOL) data += '{}{}'.format(gpg_constants.COMMIT_KEY, gpg_constants.EOL) if GPGPlugin.DEBUGGING: self.log_message('Name-Real: {}'.format(name)) self.log_message('Name-Email: {}'.format(email)) self.log_message('Expire-Date: {}{}'.format(expires_in, expiration_unit)) result_code, gpg_output, gpg_error = self.gpg_command( [gpg_constants.GEN_KEY], data=data, wait_for_results=wait_for_results) if result_code == gpg_constants.GOOD_RESULT: fingerprint = gpg_utils.parse_gen_key_results(gpg_error) if fingerprint is None: if gpg_output: self.log_message(gpg_output) if gpg_error: self.log_message(gpg_error) if gpg_output == gpg_constants.KEY_EXISTS: key_already_exists = True self.log_message('key already exists for {}'.format(email)) else: self.log_message('created key for {} <{}>: {} result code'.format(name, email, result_code)) if gpg_output: self.log_message(gpg_output) if gpg_error: self.log_message(gpg_error) result_ok = result_code == gpg_constants.GOOD_RESULT self.log_message('result ok: {}'.format(result_ok)) timed_out = result_code == gpg_constants.TIMED_OUT_RESULT self.log_message('timedout: {}'.format(timed_out)) except Exception as exception: self.handle_unexpected_exception(exception) finally: self.log_message('finished starting to gen key for {}'.format(user_id)) return result_ok, timed_out, fingerprint, key_already_exists
def add(email, encryption_program, fingerprint=None, passcode=None, source=None): ''' Add a contact and related settings. >>> # In honor of Thomas Drake, a whistleblower about Trailblazer, a NSA mass surveillance project. >>> email = '*****@*****.**' >>> encryption_software = crypto_software.get(KeyFactory.DEFAULT_ENCRYPTION_NAME) >>> contact = add(email, KeyFactory.DEFAULT_ENCRYPTION_NAME) >>> contact.email '*****@*****.**' >>> contact.user_name 'Thomas' >>> address = contact.email >>> address = '*****@*****.**' >>> contacts_crypto = ContactsCrypto.objects.get( ... contact=contact, encryption_software=encryption_software) >>> contacts_crypto is not None True >>> x = contact.delete() >>> contact = add(None, encryption_software) >>> contact is None True >>> contact = add(email, None) >>> contact.email = '*****@*****.**' >>> contact.user_name = 'Thomas' >>> get_contacts_crypto(email) [] >>> x = contact.delete() >>> contact = add(None, None) >>> contact is None True >>> contact = add('*****@*****.**', None) >>> contact.email = '*****@*****.**' >>> contact.user_name = 'test.com domain key (system use only)' >>> x = contact.delete() ''' try: new_contact = True user_name, email_address = parse_address(email) if email_address is None: contact = None else: try: contact = Contact.objects.get(email=email_address) new_contact = False # update the user name if it's been given and it differs from the name in the DB if user_name is not None and contact.user_name != user_name: contact.user_name = user_name contact.save() log_message('updated {} user name to {}'.format( email_address, user_name)) except Contact.DoesNotExist: log_message('creating a contact for {}'.format(email_address)) try: if user_name is None or len(user_name.strip()) <= 0: from goodcrypto.mail.message.metadata import is_metadata_address user_name = email_address i = user_name.find('@') # handle domain keys specially if is_metadata_address(email_address): if i > 0: email_domain = user_name[i + 1:] user_name = '{} domain key (system use only)'.format( email_domain) else: if i > 0: user_name = user_name[:i] user_name = user_name.replace('.', ' ').replace( '-', ' ').replace('_', ' ') user_name = capwords(user_name) except: pass contact = Contact.objects.create(email=email_address, user_name=user_name) except Exception: record_exception() log_message('EXCEPTION - see syr.exception.log for details') contact = None if encryption_program is None: log_message( "no encryption software defined so not creating contact's crytpo record for {}" .format(email)) else: # add a corresponding record for the contact's crypto program encryption_software = crypto_software.get(encryption_program) if contact is None or encryption_software is None: log_message('no contact and/or encryption software defined') else: try: contacts_crypto = ContactsCrypto.objects.get( contact=contact, encryption_software=encryption_software) if (fingerprint is not None and strip_fingerprint(contacts_crypto.fingerprint) != strip_fingerprint(fingerprint)): contacts_crypto.fingerprint = format_fingerprint( fingerprint) if email_in_domain(email): if contacts.crypto.source is None: contacts.crypto.source = constants.AUTO_GENERATED if contacts_crypto.source == constants.AUTO_GENERATED: contacts_crypto.verified = True contacts_crypto.save() except ContactsCrypto.DoesNotExist: # if the contact existed without any contact crypto, but was set # to never encrypt and now we have a key, then change the # outbound encrypt policy to the default if (not new_contact and contact.outbound_encrypt_policy == constants.NEVER_ENCRYPT_OUTBOUND): contact.outbound_encrypt_policy = constants.DEFAULT_OUTBOUND_ENCRYPT_POLICY contact.save() contacts_crypto = add_contacts_crypto( contact, encryption_software, fingerprint=fingerprint, source=source) log_message( "created {} crypto record for {} with {} fingerprint: {}" .format(encryption_software, email, fingerprint, contacts_crypto is not None)) except: record_exception() log_message( 'EXCEPTION - see syr.exception.log for details') except Exception: contact = None record_exception() log_message('EXCEPTION - see syr.exception.log for details') return contact