from __future__ import absolute_import, unicode_literals from datetime import date import logging import os import shutil import tempfile import gnupg from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from .exceptions import NeedPassphrase, PassphraseError from .literals import ( ERROR_MSG_NEED_PASSPHRASE, ERROR_MSG_BAD_PASSPHRASE, ERROR_MSG_GOOD_PASSPHRASE, KEY_TYPE_CHOICES, KEY_TYPE_SECRET, OUTPUT_MESSAGE_CONTAINS_PRIVATE_KEY ) from .managers import KeyManager from .settings import setting_gpg_path logger = logging.getLogger(__name__) def gpg_command(function): temporary_directory = tempfile.mkdtemp() os.chmod(temporary_directory, 0x1C0) gpg = gnupg.GPG( gnupghome=temporary_directory, gpgbinary=setting_gpg_path.value ) result = function(gpg=gpg) shutil.rmtree(temporary_directory) return result @python_2_unicode_compatible class Key(models.Model): key_data = models.TextField(verbose_name=_('Key data')) creation_date = models.DateField( editable=False, verbose_name=_('Creation date') ) expiration_date = models.DateField( blank=True, editable=False, null=True, verbose_name=_('Expiration date') ) fingerprint = models.CharField( editable=False, max_length=40, unique=True, verbose_name=_('Fingerprint') ) length = models.PositiveIntegerField( editable=False, verbose_name=_('Length') ) algorithm = models.PositiveIntegerField( editable=False, verbose_name=_('Algorithm') ) user_id = models.TextField(editable=False, verbose_name=_('User ID')) key_type = models.CharField( choices=KEY_TYPE_CHOICES, editable=False, max_length=3, verbose_name=_('Type') ) objects = KeyManager() class Meta: verbose_name = _('Key') verbose_name_plural = _('Keys') def clean(self): def import_key(gpg): return gpg.import_keys(key_data=self.key_data) import_results = gpg_command(function=import_key) if not import_results.count: raise ValidationError('Invalid key data') def get_absolute_url(self): return reverse('django_gpg:key_detail', args=(self.pk,)) def save(self, *args, **kwargs): temporary_directory = tempfile.mkdtemp() os.chmod(temporary_directory, 0x1C0) gpg = gnupg.GPG( gnupghome=temporary_directory, gpgbinary=setting_gpg_path.value ) import_results = gpg.import_keys(key_data=self.key_data) key_info = gpg.list_keys(keys=import_results.fingerprints[0])[0] logger.debug('key_info: %s', key_info) shutil.rmtree(temporary_directory) self.algorithm = key_info['algo'] self.creation_date = date.fromtimestamp(int(key_info['date'])) if key_info['expires']: self.expiration_date = date.fromtimestamp(int(key_info['expires'])) self.fingerprint = key_info['fingerprint'] self.length = int(key_info['length']) self.user_id = key_info['uids'][0] if OUTPUT_MESSAGE_CONTAINS_PRIVATE_KEY in import_results.results[0]['text']: self.key_type = KEY_TYPE_SECRET else: self.key_type = key_info['type'] super(Key, self).save(*args, **kwargs) def __str__(self): return '{} - {}'.format(self.key_id, self.user_id) def sign_file(self, file_object, passphrase=None, clearsign=True, detached=False, binary=False, output=None): temporary_directory = tempfile.mkdtemp() os.chmod(temporary_directory, 0x1C0) gpg = gnupg.GPG( gnupghome=temporary_directory, gpgbinary=setting_gpg_path.value ) import_results = gpg.import_keys(key_data=self.key_data) file_sign_results = gpg.sign_file( file=file_object, keyid=import_results.fingerprints[0], passphrase=passphrase, clearsign=clearsign, detach=detached, binary=binary, output=output ) shutil.rmtree(temporary_directory) logger.debug('file_sign_results.stderr: %s', file_sign_results.stderr) if ERROR_MSG_NEED_PASSPHRASE in file_sign_results.stderr: if ERROR_MSG_BAD_PASSPHRASE in file_sign_results.stderr: raise PassphraseError elif ERROR_MSG_GOOD_PASSPHRASE not in file_sign_results.stderr: raise NeedPassphrase return file_sign_results @property def key_id(self): return self.fingerprint[-8:]