Skip to content

Commit

Permalink
remove checksum functionality, always offer to confirm master password
Browse files Browse the repository at this point in the history
  • Loading branch information
sspiff committed Oct 16, 2014
1 parent fef77bb commit 8ce6fd2
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 218 deletions.
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,4 @@ narvi stores its saved configuration in `~/.narvi/config` as JSON. This file wi
* `clipboard-time` (integer) The number of seconds that narvi will keep the generated password on the clipboard. Default is 8.
* `default-hashscheme` (string) The default hash scheme for new salts. Default is `scrypt-18-8-1-512`.
* `default-wordscheme` (string) The default word scheme for new salts. Default is `base64-16-!@-aA1`.
* `store-checksum` (boolean) If `true`, store a checksum for new salts. Default is `false`. See [Derived Key Checksums](#derived-key-checksums) below.

## Derived Key Checksums

_Storing checksums is disabled by default because the author is not sure that storing them does not compromise the master password or the security of your accounts._

narvi can use a checksum to detect, in most cases, when you've mistyped your master password. It works like this:

If `store-checksum` is `true`, then when defining a new salt, narvi will prompt you twice for the master password. If the two entries match, then narvi will derive the key material and compute a byte-wise checksum over all of the raw key material (usually 512 bytes). narvi will store the 1-byte checksum along with the salt definition. The next time you use the salt, narvi will re-compute the checksum and compare with what was previously stored. If the checksums do not match, then you've mistyped your master password.
48 changes: 13 additions & 35 deletions src/narvi/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,9 @@ def cmd_hash(args, pwh, completer):
completer.clear_values()
else:
saltid = args.saltid
#
if saltid in pwh.user_salts:
salt = pwh.user_salts[saltid]
while True:
master = getpass.getpass(
'Master password for ' + saltid + ': ')
password, checksum = pwh.generate_password(
salt, master)
if (salt['checksum'] is None or
checksum == salt['checksum']):
break
else:
print 'ERROR: Checksum does not match.'
else:
print 'INFO:', saltid, 'not found.'
salt = {}
Expand All @@ -176,33 +167,20 @@ def cmd_hash(args, pwh, completer):
if prompt('Save?', 'y') in ['y', 'Y']:
saveit = True
salt['description'] = prompt('Description', '')
while True:
master = getpass.getpass(
'Master password for ' + saltid + ': ')
if pwh.user_settings['store-checksum']:
master2 = getpass.getpass('Again: ')
if master == master2:
break
else:
print 'ERROR: Passwords do not match.'
else:
salt['checksum'] = None
break
else:
saveit = False
master = getpass.getpass(
'Master password for ' + saltid + ': ')
if saveit:
pwh.save_config()
password, checksum = pwh.generate_password(
salt, master)
if saveit:
if pwh.user_settings['store-checksum']:
salt['checksum'] = checksum
else:
salt['checksum'] = None
pwh.user_salts[saltid] = salt
pwh.save_config()
#
while True:
master = getpass.getpass(
'Master password for ' + saltid + ': ')
master2 = getpass.getpass('Again (blank if you\'re sure): ')
if not master2 or master == master2:
break
else:
print 'ERROR: Passwords do not match.'
#
password = pwh.generate_password(salt, master)
#
clipboardtime = pwh.user_settings['clipboard-time']
with TemporaryClipboard(password):
sys.stdout.write('.' * clipboardtime)
Expand Down
176 changes: 1 addition & 175 deletions src/pwhash/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ def _default_user_config(self):
'default-hashscheme': 'scrypt-18-8-1-512',
'default-wordscheme': 'base64-16-!@-aA1',
'clipboard-time': 8,
'store-checksum': False,
'lib-version': ''
},
'salts': {},
Expand Down Expand Up @@ -244,181 +243,8 @@ def generate_password(self, sd, masterpassword):
raise PWHashError('Word function \'' + wordfid + '\' not available.')
# generate derived key
derivedkey = hashf(self, hashp, masterpassword, salt)
# compute checksum
checksum = self.generate_checksum(derivedkey)
# convert to word
password = wordf(self, wordp, derivedkey)
return (password, checksum)

def generate_checksum(self, buf):
cksum = 0
for b in buf:
cksum = (cksum + ord(b)) & 0xff
return cksum


# data
#
# globals:
# DEFAULT_HASHSCHEME
# DEFAULT_WORDSCHEME
# STORE_CHECKSUM
#
# list of SALTs:
# SALT
# DESCRIPTION
# HASHSCHEME
# WORDSCHEME
# CHECKSUM
#
# list of HASHSCHEMEs:
# HASHSCHEME
# DESCRIPTION
# FUNCTION
# PARAMS
#
# list of WORDSCHEMEs:
# WORDSCHEME
# DESCRIPTION
# FUNCTION
# PARAMS
#
# list of HASHFUNCTIONS:
# HASHFUNCTION
# f
#
# list of WORDFUNCTIONS:
# WORDFUNCTION
# f
#

# pwhash
# prompt for salt, go to hash sub cmd
#
# pwhash hash SALT
# if SALT is known:
# prompt for MASTER
# run key derivation function
# compute checksum of key
# if checksum does not match:
# back to password prompt
# convert key bytes to password
# copy password to clipboard
# else SALT is unknown:
# prompt for DESCRIPTION
# prompt for KDFARGS
# prompt for WORDIFIER
# prompt for MASTER
# prompt for MASTER
# if not match: restart
# run key derivation function
# compute CHECKSUM
# store DESCRIPTION, SALT, KDF, CHECKSUM
# convert key bytes to password
# copy password to clipboard
#
# pwhash list [-l]
# list known SALTs [with DESCRIPTION]
#
# pwhash info SALT
# list all info on SALT
#
# pwhash forget SALT
# remove SALT from cache
#
# pwhash lshashschemes [-l]
# list KDFs
#
# pwhash lswordschemes [-l]
# list wordifiers
#
# pwhash lsglobal
# pwhash getglobal
# pwhash setglobal
#
#
# data storage:
# json-encoded
# {
# 'modulepaths': [
# ]
# 'globalsettings' :{
# 'default-hashscheme': string,
# ' default-wordscheme': string,
# }
# 'salts': {
# SALT1: {
# 'value': string,
# 'description': string,
# 'hashschemeid': string,
# 'wordschemeid': string,
# 'checksum': number
# },
# SALT2: {
# 'value': string,
# 'description': string,
# 'hashschemeid ': string,
# 'wordschemeid ': string,
# 'checksum': number
# }
# }
# }
#
#
# KDFs:
#
# scrypt-16-8-1-512 (relative strength 0)
# scrypt-20-8-1-512 (10)
#
# wordifiers:
#
# base64-!@-16-aA1
# pin-4
#
#
# modules:
#
# mods/scrypt.py
# mods/base64.py
#
# each mod has one export:
#
# provides = {
# 'hashfunctions' = {
# KDFNAME: {
# 'f': function(parameters, password, salt)
# }
# },
# 'hashschemes' = {
# 'scrypt-16-8-1-512': {
# 'description': '...',
# 'hashfunctionid': 'scrypt'
# 'hashparams': {
# 'N': (1 << 16),
# 'r': 8,
# 'p': 1,
# 'dklen': 512
# }
# }
# },
# 'wordfunctions' = {
# NAME: {
# 'f': function(parameters, buf)
# }
# }
# 'wordschemes': {
# 'base64-16-!@-aA1': {
# 'description': '...',
# 'wordfunctionid': 'base64'
# 'wordparams': {
# 'pwlen': 16
# 'altchars': '!@'
# }
# }
# }
# }
#


return password


0 comments on commit 8ce6fd2

Please sign in to comment.