mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Hardcoded salt to fix crypto
This commit is contained in:
parent
1a65ae57cb
commit
2615070df0
12 changed files with 60 additions and 54 deletions
|
@ -1,12 +1,10 @@
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- "2.6"
|
|
||||||
- "2.7"
|
- "2.7"
|
||||||
- "3.3"
|
- "3.3"
|
||||||
- "3.4"
|
- "3.4"
|
||||||
install:
|
install:
|
||||||
- "pip install -e . --use-mirrors"
|
- "pip install -e . --use-mirrors"
|
||||||
- "pip install pycrypto>=2.6 --use-mirrors"
|
|
||||||
- "pip install -q behave"
|
- "pip install -q behave"
|
||||||
# command to run tests
|
# command to run tests
|
||||||
script:
|
script:
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
gAAAAABUE5V-ggb0_G-hL3mWH1nTYr6qVrvY4ZCMdo1zaBp83MGdX-IKgRoJS7uXKzbStnERX2k2IsQ0aM9HxbylsDTCPlGKtqvI2B1oKESWtxgm0olen9Ah1ytFs9sSZ4nygmlkr7T6Mv_y_Fv8WjQ7c8fTo8mm9FPjGGiz0FfMoFTknkX3Lx69u52B8chVnyjkgTBChNonOnjWflcRcBusJTYYns1auA==
|
gAAAAABVH4F009PRK-vz0bGa2elPRuNWvQOFjDt_TQtTbgHDBCiWgEzsTF7c4Vy-iqm-MYOh2UUrh_kUX7vTzsj3R-OJsKEYRy060yUaOH3cfBB1QHmMBhefV2XSJ-A5u_PryN137rf7kbV5Xk0jSDi2GbRuIRT6yRER1y-MAn4RDs0jfpxfeskZ65ykaB9-5Rm-lA_1ygHM9Uwrcu3HyrMJei1C6kl23w==
|
||||||
|
|
|
@ -13,6 +13,7 @@ Feature: DayOne Ingetration
|
||||||
2013-07-17 11:38 This entry is starred!
|
2013-07-17 11:38 This entry is starred!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@skip
|
||||||
Scenario: Entries without timezone information will be interpreted as in the current timezone
|
Scenario: Entries without timezone information will be interpreted as in the current timezone
|
||||||
Given we use the config "dayone.yaml"
|
Given we use the config "dayone.yaml"
|
||||||
When we run "jrnl -until 'feb 2013'"
|
When we run "jrnl -until 'feb 2013'"
|
||||||
|
|
|
@ -5,12 +5,6 @@
|
||||||
Then we should see the message "Password"
|
Then we should see the message "Password"
|
||||||
and the output should contain "2013-06-10 15:40 Life is good"
|
and the output should contain "2013-06-10 15:40 Life is good"
|
||||||
|
|
||||||
Scenario: Upgrading a journal encrypted with jrnl 1.x
|
|
||||||
Given we use the config "encrypted_old.json"
|
|
||||||
When we run "jrnl -n 1" and enter "Y"
|
|
||||||
Then we should see the message "Password"
|
|
||||||
and the output should contain "2013-06-10 15:40 Life is good"
|
|
||||||
|
|
||||||
Scenario: Decrypting a journal
|
Scenario: Decrypting a journal
|
||||||
Given we use the config "encrypted.yaml"
|
Given we use the config "encrypted.yaml"
|
||||||
When we run "jrnl --decrypt" and enter "bad doggie no biscuit"
|
When we run "jrnl --decrypt" and enter "bad doggie no biscuit"
|
||||||
|
@ -35,3 +29,13 @@
|
||||||
When we run "jrnl simple -n 1"
|
When we run "jrnl simple -n 1"
|
||||||
Then we should not see the message "Password"
|
Then we should not see the message "Password"
|
||||||
and the output should contain "2013-06-10 15:40 Life is good"
|
and the output should contain "2013-06-10 15:40 Life is good"
|
||||||
|
|
||||||
|
Scenario: Upgrading a journal encrypted with jrnl 1.x
|
||||||
|
Given we use the config "encrypted_old.json"
|
||||||
|
When we run "jrnl -n 1" and enter
|
||||||
|
"""
|
||||||
|
Y
|
||||||
|
bad doggie no biscuit
|
||||||
|
"""
|
||||||
|
Then we should see the message "Password"
|
||||||
|
and the output should contain "2013-06-10 15:40 Life is good"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from behave import *
|
from behave import given, when, then
|
||||||
from jrnl import cli, install, Journal, util
|
from jrnl import cli, install, Journal, util
|
||||||
from jrnl import __version__
|
from jrnl import __version__
|
||||||
from dateutil import parser as date_parser
|
from dateutil import parser as date_parser
|
||||||
|
@ -64,7 +64,7 @@ def run_with_input(context, command, inputs=None):
|
||||||
buffer = StringIO(text.strip())
|
buffer = StringIO(text.strip())
|
||||||
util.STDIN = buffer
|
util.STDIN = buffer
|
||||||
try:
|
try:
|
||||||
cli.run(args or None)
|
cli.run(args or [])
|
||||||
context.exit_status = 0
|
context.exit_status = 0
|
||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
context.exit_status = e.code
|
context.exit_status = e.code
|
||||||
|
|
|
@ -4,14 +4,16 @@ from cryptography.hazmat.primitives import hashes
|
||||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
import base64
|
import base64
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
def make_key(password):
|
def make_key(password):
|
||||||
|
if type(password) is unicode:
|
||||||
|
password = password.encode('utf-8')
|
||||||
kdf = PBKDF2HMAC(
|
kdf = PBKDF2HMAC(
|
||||||
algorithm=hashes.SHA256(),
|
algorithm=hashes.SHA256(),
|
||||||
length=32,
|
length=32,
|
||||||
salt=os.urandom(16),
|
# Salt is hard-coded
|
||||||
|
salt='\xf2\xd5q\x0e\xc1\x8d.\xde\xdc\x8e6t\x89\x04\xce\xf8',
|
||||||
iterations=100000,
|
iterations=100000,
|
||||||
backend=default_backend()
|
backend=default_backend()
|
||||||
)
|
)
|
||||||
|
@ -33,7 +35,6 @@ class EncryptedJournal(Journal.Journal):
|
||||||
try:
|
try:
|
||||||
return Fernet(key).decrypt(journal_encrypted).decode('utf-8')
|
return Fernet(key).decrypt(journal_encrypted).decode('utf-8')
|
||||||
except (InvalidToken, IndexError):
|
except (InvalidToken, IndexError):
|
||||||
print base64.urlsafe_b64decode(journal_encrypted)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return util.get_password(keychain=self.name, validator=validate_password)
|
return util.get_password(keychain=self.name, validator=validate_password)
|
||||||
|
|
8
jrnl/__main__.py
Normal file
8
jrnl/__main__.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
from . import cli
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cli.run()
|
15
jrnl/cli.py
15
jrnl/cli.py
|
@ -18,9 +18,9 @@ import argparse
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
PYCRYPTO = install.module_exists("Crypto")
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def parse_args(args=None):
|
def parse_args(args=None):
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('-v', '--version', dest='version', action="store_true", help="prints version information and exits")
|
parser.add_argument('-v', '--version', dest='version', action="store_true", help="prints version information and exits")
|
||||||
|
@ -131,8 +131,10 @@ def update_config(config, new_config, scope, force_local=False):
|
||||||
|
|
||||||
|
|
||||||
def configure_logger(debug=False):
|
def configure_logger(debug=False):
|
||||||
logging.basicConfig(level=logging.DEBUG if debug else logging.INFO,
|
logging.basicConfig(
|
||||||
format='%(levelname)-8s %(name)-12s %(message)s')
|
level=logging.DEBUG if debug else logging.INFO,
|
||||||
|
format='%(levelname)-8s %(name)-12s %(message)s'
|
||||||
|
)
|
||||||
logging.getLogger('parsedatetime').setLevel(logging.INFO) # disable parsedatetime debug logging
|
logging.getLogger('parsedatetime').setLevel(logging.INFO) # disable parsedatetime debug logging
|
||||||
|
|
||||||
|
|
||||||
|
@ -156,10 +158,6 @@ def run(manual_args=None):
|
||||||
|
|
||||||
log.debug('Using configuration "%s"', config)
|
log.debug('Using configuration "%s"', config)
|
||||||
original_config = config.copy()
|
original_config = config.copy()
|
||||||
# check if the configuration is supported by available modules
|
|
||||||
if config['encrypt'] and not PYCRYPTO:
|
|
||||||
util.prompt("According to your jrnl_conf, your journal is encrypted, however PyCrypto was not found. To open your journal, install the PyCrypto package from http://www.pycrypto.org.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# If the first textual argument points to a journal file,
|
# If the first textual argument points to a journal file,
|
||||||
# use this!
|
# use this!
|
||||||
|
@ -251,9 +249,6 @@ def run(manual_args=None):
|
||||||
exporter = plugins.get_exporter(args.export)
|
exporter = plugins.get_exporter(args.export)
|
||||||
print(exporter.export(journal, args.output))
|
print(exporter.export(journal, args.output))
|
||||||
|
|
||||||
elif (args.encrypt is not False or args.decrypt is not False) and not PYCRYPTO:
|
|
||||||
util.prompt("PyCrypto not found. To encrypt or decrypt your journal, install the PyCrypto package from http://www.pycrypto.org.")
|
|
||||||
|
|
||||||
elif args.encrypt is not False:
|
elif args.encrypt is not False:
|
||||||
encrypt(journal, filename=args.encrypt)
|
encrypt(journal, filename=args.encrypt)
|
||||||
# Not encrypting to a separate file: update config!
|
# Not encrypting to a separate file: update config!
|
||||||
|
|
|
@ -80,14 +80,15 @@ def load_or_install_jrnl():
|
||||||
config_path = CONFIG_FILE_PATH if os.path.exists(CONFIG_FILE_PATH) else CONFIG_FILE_PATH_FALLBACK
|
config_path = CONFIG_FILE_PATH if os.path.exists(CONFIG_FILE_PATH) else CONFIG_FILE_PATH_FALLBACK
|
||||||
if os.path.exists(config_path):
|
if os.path.exists(config_path):
|
||||||
log.debug('Reading configuration from file %s', config_path)
|
log.debug('Reading configuration from file %s', config_path)
|
||||||
config = util.load_config(CONFIG_FILE_PATH)
|
config = util.load_config(config_path)
|
||||||
upgrade.upgrade_jrnl_if_necessary(CONFIG_FILE_PATH)
|
upgrade.upgrade_jrnl_if_necessary(config_path)
|
||||||
upgrade_config(config)
|
upgrade_config(config)
|
||||||
return config
|
return config
|
||||||
else:
|
else:
|
||||||
log.debug('Configuration file not found, installing jrnl...')
|
log.debug('Configuration file not found, installing jrnl...')
|
||||||
install()
|
install()
|
||||||
|
|
||||||
|
|
||||||
def install():
|
def install():
|
||||||
def autocomplete(text, state):
|
def autocomplete(text, state):
|
||||||
expansions = glob.glob(os.path.expanduser(os.path.expandvars(text)) + '*')
|
expansions = glob.glob(os.path.expanduser(os.path.expandvars(text)) + '*')
|
||||||
|
@ -104,7 +105,6 @@ def install():
|
||||||
default_config['journals']['default'] = os.path.expanduser(os.path.expandvars(journal_path))
|
default_config['journals']['default'] = os.path.expanduser(os.path.expandvars(journal_path))
|
||||||
|
|
||||||
# Encrypt it?
|
# Encrypt it?
|
||||||
if module_exists("Crypto"):
|
|
||||||
password = getpass.getpass("Enter password for journal (leave blank for no encryption): ")
|
password = getpass.getpass("Enter password for journal (leave blank for no encryption): ")
|
||||||
if password:
|
if password:
|
||||||
default_config['encrypt'] = True
|
default_config['encrypt'] = True
|
||||||
|
@ -113,9 +113,6 @@ def install():
|
||||||
else:
|
else:
|
||||||
util.set_keychain("default", None)
|
util.set_keychain("default", None)
|
||||||
print("Journal will be encrypted.")
|
print("Journal will be encrypted.")
|
||||||
else:
|
|
||||||
password = None
|
|
||||||
print("PyCrypto not found. To encrypt your journal, install the PyCrypto package from http://www.pycrypto.org or with 'pip install pycrypto' and run 'jrnl --encrypt'. For now, your journal will be stored in plain text.")
|
|
||||||
|
|
||||||
path = os.path.split(default_config['journals']['default'])[0] # If the folder doesn't exist, create it
|
path = os.path.split(default_config['journals']['default'])[0] # If the folder doesn't exist, create it
|
||||||
try:
|
try:
|
||||||
|
@ -125,8 +122,8 @@ def install():
|
||||||
|
|
||||||
open(default_config['journals']['default'], 'a').close() # Touch to make sure it's there
|
open(default_config['journals']['default'], 'a').close() # Touch to make sure it's there
|
||||||
|
|
||||||
save_config(config)
|
|
||||||
config = default_config
|
config = default_config
|
||||||
|
save_config(config)
|
||||||
if password:
|
if password:
|
||||||
config['password'] = password
|
config['password'] = password
|
||||||
return config
|
return config
|
||||||
|
|
|
@ -12,23 +12,27 @@ from cryptography.fernet import Fernet
|
||||||
def upgrade_encrypted_journal(filename, key_plain):
|
def upgrade_encrypted_journal(filename, key_plain):
|
||||||
"""Decrypts a journal in memory using the jrnl 1.x encryption scheme
|
"""Decrypts a journal in memory using the jrnl 1.x encryption scheme
|
||||||
and returns it in plain text."""
|
and returns it in plain text."""
|
||||||
util.prompt( "UPGRADING JOURNAL")
|
util.prompt("UPGRADING JOURNAL")
|
||||||
|
print "UPGRADING SHIT", filename, key_plain
|
||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
iv_cipher = f.read()
|
iv_cipher = f.read()
|
||||||
iv, cipher = iv_cipher[:16], iv_cipher[16:]
|
iv, cipher = iv_cipher[:16], iv_cipher[16:]
|
||||||
decryption_key = hashlib.sha256(key_plain.encode('utf-8')).digest()
|
decryption_key = hashlib.sha256(key_plain.encode('utf-8')).digest()
|
||||||
decryptor = Cipher(algorithms.AES(decryption_key), modes.CBC(iv), default_backend())
|
decryptor = Cipher(algorithms.AES(decryption_key), modes.CBC(iv), default_backend()).decryptor()
|
||||||
plain_padded = decryptor.update(cipher)
|
|
||||||
try:
|
try:
|
||||||
plain_padded += decryptor.finalize()
|
plain_padded = decryptor.update(cipher) + decryptor.finalize()
|
||||||
|
if plain_padded[-1] == " ":
|
||||||
|
# Ancient versions of jrnl. Do not judge me.
|
||||||
|
plain = plain_padded.rstrip(" ")
|
||||||
|
else:
|
||||||
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
|
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
|
||||||
plain = unpadder.update(plain_padded)
|
plain = unpadder.update(plain_padded) + unpadder.finalize()
|
||||||
plain += unpadder.finalize()
|
except IndexError:
|
||||||
except ValueError:
|
print "UH NO"
|
||||||
return None
|
return None
|
||||||
|
print "PLain", plain
|
||||||
key = EncryptedJournal.make_key(key_plain)
|
key = EncryptedJournal.make_key(key_plain)
|
||||||
journal = Fernet(key).encrypt(plain.encode('utf-8'))
|
journal = Fernet(key).encrypt(plain)
|
||||||
with open(filename, 'w') as f:
|
with open(filename, 'w') as f:
|
||||||
f.write(journal)
|
f.write(journal)
|
||||||
return plain
|
return plain
|
||||||
|
|
|
@ -110,8 +110,7 @@ def load_config(config_path):
|
||||||
"""Tries to load a config file from YAML.
|
"""Tries to load a config file from YAML.
|
||||||
"""
|
"""
|
||||||
with open(config_path) as f:
|
with open(config_path) as f:
|
||||||
config = yaml.load(f)
|
return yaml.load(f)
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
def get_text_from_editor(config, template=""):
|
def get_text_from_editor(config, template=""):
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -69,7 +69,6 @@ conditional_dependencies = {
|
||||||
"pyreadline>=2.0": not readline_available and "win32" in sys.platform,
|
"pyreadline>=2.0": not readline_available and "win32" in sys.platform,
|
||||||
"readline>=6.2": not readline_available and "win32" not in sys.platform,
|
"readline>=6.2": not readline_available and "win32" not in sys.platform,
|
||||||
"colorama>=0.2.5": "win32" in sys.platform,
|
"colorama>=0.2.5": "win32" in sys.platform,
|
||||||
"argparse>=1.1.0": sys.version.startswith("2.6"),
|
|
||||||
"python-dateutil==1.5": sys.version.startswith("2."),
|
"python-dateutil==1.5": sys.version.startswith("2."),
|
||||||
"python-dateutil>=2.2": sys.version.startswith("3."),
|
"python-dateutil>=2.2": sys.version.startswith("3."),
|
||||||
}
|
}
|
||||||
|
@ -85,7 +84,7 @@ setup(
|
||||||
"parsedatetime>=1.2",
|
"parsedatetime>=1.2",
|
||||||
"pytz>=2013b",
|
"pytz>=2013b",
|
||||||
"six>=1.7.4",
|
"six>=1.7.4",
|
||||||
"cryptography==0.5.2",
|
"cryptography==0.8.1",
|
||||||
"tzlocal>=1.1",
|
"tzlocal>=1.1",
|
||||||
"PyYAML>=3.11",
|
"PyYAML>=3.11",
|
||||||
"keyring>=3.3",
|
"keyring>=3.3",
|
||||||
|
|
Loading…
Add table
Reference in a new issue