Unifies encryption between python versions

This commit is contained in:
Manuel Ebert 2013-07-22 21:24:19 +02:00
parent dbf5caa971
commit 84556c178a
4 changed files with 23 additions and 22 deletions

View file

@ -1,6 +1,10 @@
Changelog Changelog
========= =========
### 1.4.0
* [Improved] Unifies encryption between Python 2 and 3. If you have problems reading encrypted journals afterwards, first decrypt your journal with the __old__ jrnl version (install with `pip install jrnl==1.3.1`, then `jrnl --decrypt`), upgrade jrnl (`pip install jrnl --upgrade`) and encrypt it again (`jrnl --encrypt`).
### 1.3.2 ### 1.3.2
* [Improved] Everything that is not direct output of jrnl will be written stderr to improve integration * [Improved] Everything that is not direct output of jrnl will be written stderr to improve integration

View file

@ -3,8 +3,8 @@
try: from . import Entry try: from . import Entry
except (SystemError, ValueError): import Entry except (SystemError, ValueError): import Entry
try: from .util import get_local_timezone, prompt, getpass try: from . import util
except (SystemError, ValueError): from util import get_local_timezone, prompt, getpass except (SystemError, ValueError): import util
import codecs import codecs
import os import os
try: import parsedatetime.parsedatetime_consts as pdt try: import parsedatetime.parsedatetime_consts as pdt
@ -18,7 +18,7 @@ import sys
import glob import glob
try: try:
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.Random import random, atfork from Crypto import Random
crypto_installed = True crypto_installed = True
except ImportError: except ImportError:
crypto_installed = False crypto_installed = False
@ -75,32 +75,29 @@ class Journal(object):
try: try:
plain = crypto.decrypt(cipher[16:]) plain = crypto.decrypt(cipher[16:])
except ValueError: except ValueError:
prompt("ERROR: Your journal file seems to be corrupted. You do have a backup, don't you?") util.prompt("ERROR: Your journal file seems to be corrupted. You do have a backup, don't you?")
sys.exit(-1) sys.exit(-1)
if plain[-1] != " ": # Journals are always padded padding = " ".encode("utf-8")
if not plain.endswith(padding): # Journals are always padded
return None return None
else: else:
return plain return plain.decode("utf-8")
def _encrypt(self, plain): def _encrypt(self, plain):
"""Encrypt a plaintext string using self.key as the key""" """Encrypt a plaintext string using self.key as the key"""
if not crypto_installed: if not crypto_installed:
sys.exit("Error: PyCrypto is not installed.") sys.exit("Error: PyCrypto is not installed.")
atfork() # A seed for PyCrypto Random.atfork() # A seed for PyCrypto
iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16)) iv = Random.new().read(AES.block_size)
print("iv", iv, len(iv))
crypto = AES.new(self.key, AES.MODE_CBC, iv) crypto = AES.new(self.key, AES.MODE_CBC, iv)
if len(plain) % 16 != 0: plain = plain.encode("utf-8")
plain += " " * (16 - len(plain) % 16) plain += b" " * (AES.block_size - len(plain) % AES.block_size)
else: # Always pad so we can detect properly decrypted files :)
plain += " " * 16
return iv + crypto.encrypt(plain) return iv + crypto.encrypt(plain)
def make_key(self, prompt="Password: "): def make_key(self, prompt="Password: "):
"""Creates an encryption key from the default password or prompts for a new password.""" """Creates an encryption key from the default password or prompts for a new password."""
password = self.config['password'] or getpass(prompt) password = self.config['password'] or util.getpass(prompt)
print("GOT PWD", password) self.key = hashlib.sha256(password.encode("utf-8")).digest()
self.key = hashlib.sha256(password.encode('utf-8')).digest()
def open(self, filename=None): def open(self, filename=None):
"""Opens the journal file defined in the config and parses it into a list of Entries. """Opens the journal file defined in the config and parses it into a list of Entries.
@ -119,9 +116,9 @@ class Journal(object):
attempts += 1 attempts += 1
self.config['password'] = None # This password doesn't work. self.config['password'] = None # This password doesn't work.
if attempts < 3: if attempts < 3:
prompt("Wrong password, try again.") util.prompt("Wrong password, try again.")
else: else:
prompt("Extremely wrong password.") util.prompt("Extremely wrong password.")
sys.exit(-1) sys.exit(-1)
journal = decrypted journal = decrypted
else: else:
@ -318,7 +315,7 @@ class DayOne(Journal):
try: try:
timezone = pytz.timezone(dict_entry['Time Zone']) timezone = pytz.timezone(dict_entry['Time Zone'])
except pytz.exceptions.UnknownTimeZoneError: except pytz.exceptions.UnknownTimeZoneError:
timezone = pytz.timezone(get_local_timezone()) timezone = pytz.timezone(util.get_local_timezone())
date = dict_entry['Creation Date'] date = dict_entry['Creation Date']
date = date + timezone.utcoffset(date) date = date + timezone.utcoffset(date)
entry = self.new_entry(raw=dict_entry['Entry Text'], date=date, sort=False) entry = self.new_entry(raw=dict_entry['Entry Text'], date=date, sort=False)
@ -344,7 +341,7 @@ class DayOne(Journal):
'Creation Date': utc_time, 'Creation Date': utc_time,
'Starred': entry.starred if hasattr(entry, 'starred') else False, 'Starred': entry.starred if hasattr(entry, 'starred') else False,
'Entry Text': entry.title+"\n"+entry.body, 'Entry Text': entry.title+"\n"+entry.body,
'Time Zone': get_local_timezone(), 'Time Zone': util.get_local_timezone(),
'UUID': new_uuid 'UUID': new_uuid
} }
plistlib.writePlist(entry_plist, filename) plistlib.writePlist(entry_plist, filename)

View file

@ -7,7 +7,7 @@ jrnl is a simple journal application for your command line.
""" """
__title__ = 'jrnl' __title__ = 'jrnl'
__version__ = '1.3.2' __version__ = '1.4.0'
__author__ = 'Manuel Ebert' __author__ = 'Manuel Ebert'
__license__ = 'MIT License' __license__ = 'MIT License'
__copyright__ = 'Copyright 2013 Manuel Ebert' __copyright__ = 'Copyright 2013 Manuel Ebert'

View file

@ -22,7 +22,7 @@ def getpass(prompt):
def u(s): def u(s):
"""Mock unicode function for python 2 and 3 compatibility.""" """Mock unicode function for python 2 and 3 compatibility."""
return s if PY3 else unicode(s, "unicode_escape") return s if PY3 or type(s) is unicode else unicode(s, "unicode_escape")
def prompt(msg): def prompt(msg):
"""Prints a message to the std err stream defined in util.""" """Prints a message to the std err stream defined in util."""