Disallow old encryption protocol and format logging

This commit is contained in:
Tanner Collin 2017-10-09 00:01:13 -06:00
parent 30fe994c68
commit 0121490b0d
3 changed files with 32 additions and 24 deletions

View File

@ -4,6 +4,7 @@ from binascii import hexlify, unhexlify
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.Random import random from Crypto.Random import random
from copy import deepcopy from copy import deepcopy
import sys
class EncryptionHelper: class EncryptionHelper:
def pure_generatePasswordAndKey(self, password, pw_salt, pw_cost): def pure_generatePasswordAndKey(self, password, pw_salt, pw_cost):
@ -15,6 +16,7 @@ class EncryptionHelper:
pw = output[0 : split_length] pw = output[0 : split_length]
mk = output[split_length : split_length * 2] mk = output[split_length : split_length * 2]
ak = output[split_length * 2 : split_length * 3] ak = output[split_length * 2 : split_length * 3]
return dict(pw=pw, mk=mk, ak=ak) return dict(pw=pw, mk=mk, ak=ak)
def encryptDirtyItems(self, dirty_items, keys): def encryptDirtyItems(self, dirty_items, keys):
@ -36,6 +38,7 @@ class EncryptionHelper:
enc_item = deepcopy(item) enc_item = deepcopy(item)
enc_item['content'] = self.pure_encryptString002(content, item_ek, item_ak, uuid) enc_item['content'] = self.pure_encryptString002(content, item_ek, item_ak, uuid)
enc_item['enc_item_key'] = self.pure_encryptString002(item_key, keys['mk'], keys['ak'], uuid) enc_item['enc_item_key'] = self.pure_encryptString002(item_key, keys['mk'], keys['ak'], uuid)
return enc_item return enc_item
def pure_decryptItem(self, item, keys): def pure_decryptItem(self, item, keys):
@ -46,7 +49,13 @@ class EncryptionHelper:
if not content: if not content:
return item return item
if content[:3] == '002': if content[:3] == '001':
print('Old encryption protocol detected. This version is not '
'supported by standardnotes-fs. Please resync all of '
'your notes by following the instructions here:\n'
'https://standardnotes.org/help/resync')
sys.exit(1)
elif content[:3] == '002':
item_key = self.pure_decryptString002(enc_item_key, keys['mk'], keys['ak'], uuid) item_key = self.pure_decryptString002(enc_item_key, keys['mk'], keys['ak'], uuid)
item_key_length = len(item_key) item_key_length = len(item_key)
item_ek = item_key[:item_key_length//2] item_ek = item_key[:item_key_length//2]
@ -54,10 +63,13 @@ class EncryptionHelper:
dec_content = self.pure_decryptString002(content, item_ek, item_ak, uuid) dec_content = self.pure_decryptString002(content, item_ek, item_ak, uuid)
else: else:
print('Invalid protocol version.') print('Invalid protocol version. This could indicate tampering or '
'that something is wrong with the server. Exiting.')
sys.exit(1)
dec_item = deepcopy(item) dec_item = deepcopy(item)
dec_item['content'] = json.loads(dec_content) dec_item['content'] = json.loads(dec_content)
return dec_item return dec_item
def pure_encryptString002(self, string_to_encrypt, encryption_key, auth_key, uuid): def pure_encryptString002(self, string_to_encrypt, encryption_key, auth_key, uuid):
@ -87,19 +99,22 @@ class EncryptionHelper:
ciphertext = components[4] ciphertext = components[4]
if local_uuid != uuid: if local_uuid != uuid:
print('UUID does not match.') print('UUID does not match. This could indicate tampering or '
return 'that something is wrong with the server. Exiting.')
sys.exit(1)
string_to_auth = ':'.join([version, uuid, IV, ciphertext]) string_to_auth = ':'.join([version, uuid, IV, ciphertext])
local_auth_hash = hmac.new(unhexlify(auth_key), string_to_auth.encode(), 'sha256').digest() local_auth_hash = hmac.new(unhexlify(auth_key), string_to_auth.encode(), 'sha256').digest()
local_auth_hash = hexlify(local_auth_hash).decode() local_auth_hash = hexlify(local_auth_hash).decode()
if local_auth_hash != auth_hash: if local_auth_hash != auth_hash:
print('Message has been tampered with.') print('Auth hash does not match. This could indicate tampering or '
return 'that something is wrong with the server. Exiting.')
sys.exit(1)
cipher = AES.new(unhexlify(encryption_key), AES.MODE_CBC, unhexlify(IV)) cipher = AES.new(unhexlify(encryption_key), AES.MODE_CBC, unhexlify(IV))
result = cipher.decrypt(b64decode(ciphertext)) result = cipher.decrypt(b64decode(ciphertext))
result = result[:-result[-1]] # remove PKCS#7 padding result = result[:-result[-1]] # remove PKCS#7 padding
result = result.decode()
return result.decode() return result

View File

@ -139,12 +139,3 @@ class StandardNotesFUSE(LoggingMixIn, Operations):
def utimens(self, path, times=None): def utimens(self, path, times=None):
return 0 return 0
if __name__ == '__main__':
if len(argv) != 2:
print('usage: %s <mountpoint>' % argv[0])
exit(1)
logging.basicConfig(level=logging.DEBUG)
fuse = FUSE(StandardNotesFUSE(), argv[1], foreground=True, nothreads=True)

View File

@ -53,11 +53,13 @@ def main():
# configure logging # configure logging
if args.verbosity == 1: if args.verbosity == 1:
logging.basicConfig(level=logging.INFO) log_level = logging.INFO
elif args.verbosity == 2: elif args.verbosity == 2:
logging.basicConfig(level=logging.DEBUG) log_level = logging.DEBUG
else: else:
logging.basicConfig(level=logging.CRITICAL) log_level = logging.CRITICAL
logging.basicConfig(level=log_level,
format='%(levelname)-8s: %(message)s')
if args.verbosity: args.foreground = True if args.verbosity: args.foreground = True
config_file = args.config if args.config else CONFIG_FILE config_file = args.config if args.config else CONFIG_FILE
@ -74,7 +76,7 @@ def main():
# make sure mountpoint is specified # make sure mountpoint is specified
if not args.mountpoint: if not args.mountpoint:
logging.critical('No mountpoint specified.') print('No mountpoint specified.')
sys.exit(1) sys.exit(1)
# load config file settings # load config file settings
@ -85,7 +87,7 @@ def main():
logging.info(log_msg % str(config_file.parent)) logging.info(log_msg % str(config_file.parent))
except OSError: except OSError:
log_msg = 'Error creating config file directory "%s".' log_msg = 'Error creating config file directory "%s".'
logging.critical(log_msg % str(config_file.parent)) print(log_msg % str(config_file.parent))
sys.exit(1) sys.exit(1)
try: try:
@ -130,7 +132,7 @@ def main():
login_success = True login_success = True
except: except:
log_msg = 'Failed to log into account "%s".' log_msg = 'Failed to log into account "%s".'
logging.critical(log_msg % username) print(log_msg % username)
login_success = False login_success = False
# write settings back if good, clear if not # write settings back if good, clear if not
@ -145,11 +147,11 @@ def main():
logging.info(log_msg % str(config_file)) logging.info(log_msg % str(config_file))
else: else:
log_msg = 'Clearing config file "%s".' log_msg = 'Clearing config file "%s".'
logging.info(log_msg % username) logging.info(log_msg % config_file)
config_file.chmod(0o600) config_file.chmod(0o600)
except OSError: except OSError:
log_msg = 'Unable to write config file "%s".' log_msg = 'Unable to write config file "%s".'
logging.error(log_msg % str(config_file)) logging.warning(log_msg % str(config_file))
if login_success: if login_success:
logging.info('Starting FUSE filesystem.') logging.info('Starting FUSE filesystem.')