mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-12 01:18:31 +02:00
Basic support for cryptography.io
Still needs better hash function
This commit is contained in:
parent
4ea5ae80b5
commit
84d886d0ef
3 changed files with 39 additions and 58 deletions
|
@ -1,57 +1,11 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import sys
|
|
||||||
from . import Journal, util
|
from . import Journal, util
|
||||||
try:
|
from cryptography.fernet import Fernet, InvalidToken
|
||||||
from Crypto.Cipher import AES
|
import base64
|
||||||
from Crypto import Random
|
|
||||||
crypto_installed = True
|
|
||||||
except ImportError:
|
|
||||||
crypto_installed = False
|
|
||||||
|
|
||||||
|
|
||||||
def make_key(password):
|
def make_key(password):
|
||||||
return hashlib.sha256(password.encode("utf-8")).digest()
|
return base64.urlsafe_b64encode(hashlib.sha256(password.encode("utf-8")).digest())
|
||||||
|
|
||||||
|
|
||||||
def _decrypt(cipher, key):
|
|
||||||
"""Decrypts a cipher string using self.key as the key and the first 16 byte of the cipher as the IV"""
|
|
||||||
if not crypto_installed:
|
|
||||||
sys.exit("Error: PyCrypto is not installed.")
|
|
||||||
if not cipher:
|
|
||||||
return ""
|
|
||||||
crypto = AES.new(key, AES.MODE_CBC, cipher[:16])
|
|
||||||
try:
|
|
||||||
plain = crypto.decrypt(cipher[16:])
|
|
||||||
except ValueError:
|
|
||||||
util.prompt("ERROR: Your journal file seems to be corrupted. You do have a backup, don't you?")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
padding_length = util.byte2int(plain[-1])
|
|
||||||
if padding_length > AES.block_size and padding_length != 32:
|
|
||||||
# 32 is the space character and is kept for backwards compatibility
|
|
||||||
return None
|
|
||||||
elif padding_length == 32:
|
|
||||||
plain = plain.strip()
|
|
||||||
elif plain[-padding_length:] != util.int2byte(padding_length) * padding_length:
|
|
||||||
# Invalid padding!
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
plain = plain[:-padding_length]
|
|
||||||
|
|
||||||
return plain.decode("utf-8")
|
|
||||||
|
|
||||||
|
|
||||||
def _encrypt(plain, key):
|
|
||||||
"""Encrypt a plaintext string using key"""
|
|
||||||
if not crypto_installed:
|
|
||||||
sys.exit("Error: PyCrypto is not installed.")
|
|
||||||
Random.atfork() # A seed for PyCrypto
|
|
||||||
iv = Random.new().read(AES.block_size)
|
|
||||||
crypto = AES.new(key, AES.MODE_CBC, iv)
|
|
||||||
plain = plain.encode("utf-8")
|
|
||||||
padding_length = AES.block_size - len(plain) % AES.block_size
|
|
||||||
plain += util.int2byte(padding_length) * padding_length
|
|
||||||
return iv + crypto.encrypt(plain)
|
|
||||||
|
|
||||||
|
|
||||||
class EncryptedJournal(Journal.Journal):
|
class EncryptedJournal(Journal.Journal):
|
||||||
|
@ -60,18 +14,20 @@ class EncryptedJournal(Journal.Journal):
|
||||||
self.config['encrypt'] = True
|
self.config['encrypt'] = True
|
||||||
|
|
||||||
def _load(self, filename):
|
def _load(self, filename):
|
||||||
with open(filename, "rb") as f:
|
with open(filename) as f:
|
||||||
journal_encrypted = f.read()
|
journal_encrypted = f.read()
|
||||||
|
|
||||||
def validate_password(password):
|
def validate_password(password):
|
||||||
key = make_key(password)
|
key = make_key(password)
|
||||||
return _decrypt(journal_encrypted, key)
|
try:
|
||||||
|
return Fernet(key).decrypt(journal_encrypted)
|
||||||
|
except InvalidToken:
|
||||||
|
return None
|
||||||
|
|
||||||
return util.get_password(keychain=self.name, validator=validate_password)
|
return util.get_password(keychain=self.name, validator=validate_password)
|
||||||
|
|
||||||
def _store(self, filename, text):
|
def _store(self, filename, text):
|
||||||
key = make_key(self.config['password'])
|
key = make_key(self.config['password'])
|
||||||
journal = _encrypt(text, key)
|
journal = Fernet(key).encrypt(text)
|
||||||
|
with open(filename, 'w') as f:
|
||||||
with open(filename, 'wb') as f:
|
|
||||||
f.write(journal)
|
f.write(journal)
|
||||||
|
|
|
@ -1,3 +1,30 @@
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives import padding
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
import hashlib
|
||||||
|
import util
|
||||||
|
from . import __version__
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def upgrade_encrypted_journal(filename, key_plain):
|
||||||
|
"""Decrypts a journal in memory using the jrnl 1.x encryption scheme
|
||||||
|
and returns it in plain text."""
|
||||||
|
with open(filename) as f:
|
||||||
|
iv_cipher = f.read()
|
||||||
|
iv, cipher = iv_cipher[:16], iv_cipher[16:]
|
||||||
|
decryption_key = hashlib.sha256(key_plain.encode('utf-8')).digest()
|
||||||
|
decryptor = Cipher(algorithms.AES(decryption_key), modes.CBC(iv), default_backend())
|
||||||
|
plain_padded = decryptor.update(cipher)
|
||||||
|
try:
|
||||||
|
plain_padded += decryptor.finalize()
|
||||||
|
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
|
||||||
|
plain = unpadder.update(plain_padded)
|
||||||
|
plain += unpadder.finalize()
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
return plain
|
||||||
|
|
||||||
|
|
||||||
def upgrade_jrnl_if_necessary(config_path):
|
def upgrade_jrnl_if_necessary(config_path):
|
||||||
with open(config_path) as f:
|
with open(config_path) as f:
|
||||||
config = f.read()
|
config = f.read()
|
||||||
|
|
6
setup.py
6
setup.py
|
@ -84,14 +84,12 @@ setup(
|
||||||
"pyxdg>=0.19",
|
"pyxdg>=0.19",
|
||||||
"parsedatetime>=1.2",
|
"parsedatetime>=1.2",
|
||||||
"pytz>=2013b",
|
"pytz>=2013b",
|
||||||
"six>=1.6.1",
|
"six>=1.7.4",
|
||||||
|
"cryptography==0.5.2",
|
||||||
"tzlocal>=1.1",
|
"tzlocal>=1.1",
|
||||||
"PyYAML>=3.11",
|
"PyYAML>=3.11",
|
||||||
"keyring>=3.3",
|
"keyring>=3.3",
|
||||||
] + [p for p, cond in conditional_dependencies.items() if cond],
|
] + [p for p, cond in conditional_dependencies.items() if cond],
|
||||||
extras_require = {
|
|
||||||
"encrypted": "pycrypto>=2.6"
|
|
||||||
},
|
|
||||||
long_description=__doc__,
|
long_description=__doc__,
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
|
|
Loading…
Add table
Reference in a new issue