Skip to content

Commit b721d87

Browse files
committed
Rework logic for simpler base-salt UX
1 parent 2262fb7 commit b721d87

File tree

4 files changed

+233
-76
lines changed

4 files changed

+233
-76
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ The format is based on [Keep a Changelog][1], and this project adheres to
1414

1515
- Add support for pbkdf2
1616
- Add support for user specified digest
17-
- Add support for new configured salt method
17+
- Add support for new configured base salt
1818
- Add .transcrypt versioned directory
1919
- Support for OpenSSL 3.x
2020
- Add support for development editable install

contrib/zsh/_transcrypt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ _transcrypt() {
1212
'(- 1 *)'{-h,--help}'[view help message]' \
1313
'(-c --cipher -d --display -f --flush-credentials -u --uninstall)'{-c,--cipher=}'[specify encryption cipher]:cipher:->cipher' \
1414
'(-md --digest -d --display -f --flush-credentials -u --uninstall)'{-md,--digest=}'[specify encryption digest]:digest' \
15-
'(-sm --salt-method -d --display -f --flush-credentials -u --uninstall)'{-md,--digest=}'[specify salt-method]:salt-method' \
15+
'(-bs --base-salt -d --display -f --flush-credentials -u --uninstall)'{-md,--digest=}'[specify base-salt]:base-salt' \
1616
'(-cs --config-salt -d --display -f --flush-credentials -u --uninstall)'{-md,--digest=}'[specify config-salt]:config-salt' \
1717
'(-pbkdf2 --kdf -d --display -f --flush-credentials -u --uninstall)'{-md,--digest=}'[specify use-pbkdf2]:use-pbkdf2' \
1818
'(-p --password -d --display -f --flush-credentials -u --uninstall)'{-p,--password=}'[specify encryption password]:password:' \

tests/test_transcrypt.py

Lines changed: 133 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,19 @@ def _cmd(self, command, shell=False, check=True, verbose=None):
8484
shell=shell, check=check)
8585

8686
def _config_args(self):
87-
arg_templates = [
88-
"-c", self.config['cipher'],
89-
"-p", self.config['password'],
90-
"-md", self.config['digest'],
91-
"--kdf", self.config['kdf'],
92-
"-bs", self.config['base_salt'],
87+
flags_and_keys = [
88+
('-c', 'cipher'),
89+
('-p', 'password'),
90+
('-md', 'digest'),
91+
('--kdf', 'kdf'),
92+
('-bs', 'base_salt'),
9393
]
94-
args = [template.format(**self.config) for template in arg_templates]
94+
args = []
95+
for flag, key in flags_and_keys:
96+
value = self.config[key]
97+
if value is not None:
98+
args.append(flag)
99+
args.append(value)
95100
return args
96101

97102
def is_configured(self):
@@ -144,7 +149,8 @@ def version(self):
144149
return self._cmd(f'{self.transcript_exe} --version')['out'].rstrip()
145150

146151
def _crypt_dir(self):
147-
info = self._cmd('git config --local transcrypt.crypt-dir', check=0)
152+
info = self._cmd('git config --local transcrypt.crypt-dir', check=0,
153+
verbose=0)
148154
if info['err'] == 0:
149155
crypt_dpath = ub.Path(info['out'].strip())
150156
else:
@@ -198,13 +204,15 @@ def upgrade(self):
198204
return self._cmd(f'{self.transcript_exe} --upgrade -y')
199205

200206
def _load_unversioned_config(self):
207+
if self.verbose > 0:
208+
print('Loading unversioned config')
201209
local_config = {
202-
'cipher': self._cmd('git config --get --local transcrypt.cipher')['out'].strip(),
203-
'digest': self._cmd('git config --get --local transcrypt.digest')['out'].strip(),
204-
'kdf': self._cmd('git config --get --local transcrypt.kdf')['out'].strip(),
205-
'base_salt': self._cmd('git config --get --local transcrypt.base-salt')['out'].strip(),
206-
'password': self._cmd('git config --get --local transcrypt.password')['out'].strip(),
207-
'openssl_path': self._cmd('git config --get --local transcrypt.openssl-path')['out'].strip(),
210+
'cipher': self._cmd('git config --get --local transcrypt.cipher', verbose=0)['out'].strip(),
211+
'digest': self._cmd('git config --get --local transcrypt.digest', verbose=0)['out'].strip(),
212+
'kdf': self._cmd('git config --get --local transcrypt.kdf', verbose=0)['out'].strip(),
213+
'base_salt': self._cmd('git config --get --local transcrypt.base-salt', verbose=0)['out'].strip(),
214+
'password': self._cmd('git config --get --local transcrypt.password', verbose=0)['out'].strip(),
215+
'openssl_path': self._cmd('git config --get --local transcrypt.openssl-path', verbose=0)['out'].strip(),
208216
}
209217
return local_config
210218

@@ -237,7 +245,7 @@ def setup(self):
237245
self._setup_gpghome()
238246
self._setup_gitrepo()
239247
self._setup_contents()
240-
if self.verbose > 2:
248+
if self.verbose > 1:
241249
self._show_manual_env_setup()
242250
return self
243251

@@ -314,6 +322,16 @@ def _show_manual_env_setup(self):
314322
class TestCases:
315323
"""
316324
Unit tests to be applied to different transcrypt configurations
325+
326+
xdoctest -m tests/test_transcrypt.py TestCases
327+
328+
Example:
329+
>>> from test_transcrypt import * # NOQA
330+
>>> self = TestCases(verbose=2)
331+
>>> self.setup()
332+
>>> self.sandbox._show_manual_env_setup()
333+
>>> self.test_round_trip()
334+
>>> self.test_export_gpg()
317335
"""
318336

319337
def __init__(self, config=None, dpath=None, verbose=0):
@@ -412,7 +430,7 @@ def test_legacy_defaults():
412430
'password': 'correct horse battery staple',
413431
'digest': 'md5',
414432
'kdf': 'none',
415-
'base_salt': 'password',
433+
'base_salt': '',
416434
}
417435
verbose = 1
418436
self = TestCases(config=config, verbose=verbose)
@@ -437,68 +455,148 @@ def test_secure_defaults():
437455

438456

439457
def test_configured_salt_changes_on_rekey():
458+
"""
459+
CommandLine:
460+
xdoctest -m tests/test_transcrypt.py test_configured_salt_changes_on_rekey
461+
"""
440462
config = {
441463
'cipher': 'aes-256-cbc',
442464
'password': 'correct horse battery staple',
443465
'digest': 'sha512',
444466
'kdf': 'pbkdf2',
445467
'base_salt': 'random',
446468
}
447-
verbose = 1
469+
verbose = 2
448470
self = TestCases(config=config, verbose=verbose)
449471
self.setup()
450472
before_config = self.tc._load_unversioned_config()
451473
self.tc.rekey({'password': '12345', 'base_salt': ''})
452474
self.sandbox.git.commit('-am commit rekey')
453475
after_config = self.tc._load_unversioned_config()
454-
assert before_config['password'] != after_config['password']
476+
assert before_config['password'] != after_config['password'], 'password should have changed!'
477+
assert before_config['base_salt'] != after_config['base_salt'], 'salt should have changed!'
455478
assert before_config['cipher'] == after_config['cipher']
456479
assert before_config['kdf'] == after_config['kdf']
457-
assert before_config['base_salt'] == after_config['base_salt']
458480
assert before_config['openssl_path'] == after_config['openssl_path']
459481

460482

483+
def test_unspecified_salt_without_kdf():
484+
"""
485+
In this case the salt should default to the password method
486+
"""
487+
config = {
488+
'cipher': 'aes-256-cbc',
489+
'password': 'correct horse battery staple',
490+
'digest': 'sha512',
491+
'kdf': '',
492+
'base_salt': None,
493+
}
494+
verbose = 2
495+
self = TestCases(config=config, verbose=verbose)
496+
self.setup()
497+
config1 = self.tc._load_unversioned_config()
498+
assert config1['base_salt'] == 'password'
499+
500+
501+
def test_unspecified_salt_with_kdf():
502+
config = {
503+
'cipher': 'aes-256-cbc',
504+
'password': 'correct horse battery staple',
505+
'digest': 'sha512',
506+
'kdf': 'pbkdf2',
507+
'base_salt': None,
508+
}
509+
verbose = 1
510+
self = TestCases(config=config, verbose=verbose)
511+
self.setup()
512+
config1 = self.tc._load_unversioned_config()
513+
assert len(config1['base_salt']) == 64
514+
515+
516+
def test_salt_changes_when_kdf_changes():
517+
config = {
518+
'cipher': 'aes-256-cbc',
519+
'password': 'correct horse battery staple',
520+
'digest': 'sha512',
521+
'kdf': '',
522+
'base_salt': None,
523+
}
524+
verbose = 2
525+
self = TestCases(config=config, verbose=verbose)
526+
self.setup()
527+
config1 = self.tc._load_unversioned_config()
528+
assert config1['base_salt'] == 'password'
529+
# Test rekey, base-salt should still be password
530+
self.tc.rekey({'password': '12345'})
531+
config2 = self.tc._load_unversioned_config()
532+
assert config2['base_salt'] == 'password'
533+
self.sandbox.git.commit('-am commit rekey')
534+
535+
# Test rekey with kdf=pbkdf2 base-salt should now randomize
536+
self.tc.rekey({'password': '12345', 'kdf': 'pbkdf2', 'base_salt': None})
537+
config3 = self.tc._load_unversioned_config()
538+
assert len(config3['base_salt']) == 64, 'should have had new random salt'
539+
self.sandbox.git.commit('-am commit rekey')
540+
541+
# Test rekey going back to no kdf
542+
self.tc.rekey({'password': '12345', 'kdf': 'none', 'base_salt': None})
543+
config4 = self.tc._load_unversioned_config()
544+
assert config4['base_salt'] == 'password'
545+
546+
461547
def test_configuration_grid():
462548
"""
463549
CommandLine:
464550
xdoctest -m tests/test_transcrypt.py test_configuration_grid
465-
466-
Example:
467-
>>> from test_transcrypt import * # NOQA
468-
>>> self = TestCases()
469-
>>> self.setup()
470-
>>> self.sandbox._show_manual_env_setup()
471-
>>> self.test_round_trip()
472-
>>> self.test_export_gpg()
473551
"""
474552
# Test that transcrypt works under a variety of config conditions
475553
basis = {
476554
'cipher': ['aes-256-cbc', 'aes-128-ecb'],
477555
'password': ['correct horse battery staple'],
478556
'digest': ['md5', 'sha256'],
479557
'kdf': ['none', 'pbkdf2'],
480-
'base_salt': ['password', 'random', 'mylittlecustomsalt'],
558+
'base_salt': ['password', 'random', 'mylittlecustomsalt', None],
481559
}
560+
482561
test_grid = list(ub.named_product(basis))
483-
verbose = 3
562+
563+
def validate_test_grid(params):
564+
if params['kdf'] == 'none' and params['base_salt'] != 'password':
565+
return False
566+
if params['kdf'] != 'none' and params['base_salt'] == 'password':
567+
return False
568+
return True
569+
570+
# Remove invalid configs
571+
valid_test_grid = list(filter(validate_test_grid, test_grid))
572+
print('valid_test_grid = {}'.format(ub.repr2(valid_test_grid, sort=0, nl=1)))
573+
574+
verbose = 2
484575
dpath = 'special:temp'
485576
dpath = 'special:cache'
486-
for params in ub.ProgIter(test_grid, desc='test configs', freq=1):
577+
for params in ub.ProgIter(valid_test_grid, desc='test configs', freq=1,
578+
verbose=verbose + 1):
579+
if verbose:
580+
print('\n\n')
581+
print('=================')
582+
print('params = {}'.format(ub.repr2(params, nl=1)))
583+
print('=================')
584+
487585
config = params.copy()
488586
self = TestCases(config=config, dpath=dpath, verbose=verbose)
489587
self.setup()
490-
if 1:
491-
# Manual debug
492-
self.sandbox._show_manual_env_setup()
493-
494588
self.test_round_trip()
495589
self.test_export_gpg()
496590
self.test_rekey()
497-
591+
if verbose:
592+
print('=================')
498593

499594
if __name__ == '__main__':
500595
"""
501596
CommandLine:
502597
python tests/test_transcrypt.py
598+
599+
# Runs everything
600+
pytest tests/test_transcrypt.py -v
503601
"""
504602
test_configuration_grid()

0 commit comments

Comments
 (0)