mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Unifies encryption between python versions
This commit is contained in:
parent
dbf5caa971
commit
84556c178a
4 changed files with 23 additions and 22 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
Loading…
Add table
Reference in a new issue