Test with GnuPG 2 (#179)
Take advantage of pinentry-mock to obtain passphrases, instead of using "expect" (which requires GnuPG 1).
This commit is contained in:
parent
e5ff95d09c
commit
5d484ca825
1 changed files with 111 additions and 64 deletions
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pipes
|
import pipes
|
||||||
|
import time
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
KEY_FILE = 'test/test_key'
|
KEY_FILE = 'test/test_key'
|
||||||
|
@ -13,26 +14,50 @@ PASSPHRASE = 'ExamplePassword'
|
||||||
pytestmark = pytest.mark.usefixtures('config_git')
|
pytestmark = pytest.mark.usefixtures('config_git')
|
||||||
|
|
||||||
|
|
||||||
def add_asymmetric_key():
|
def add_asymmetric_key(runner, gnupg):
|
||||||
"""Add asymmetric key"""
|
"""Add asymmetric key"""
|
||||||
os.system(f'gpg --import {pipes.quote(KEY_FILE)}')
|
env = os.environ.copy()
|
||||||
os.system(f'gpg --import-ownertrust < {pipes.quote(KEY_TRUST)}')
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
runner(
|
||||||
|
['gpg', '--import', pipes.quote(KEY_FILE)],
|
||||||
|
env=env,
|
||||||
|
shell=True,
|
||||||
|
)
|
||||||
|
runner(
|
||||||
|
['gpg', '--import-ownertrust', '<', pipes.quote(KEY_TRUST)],
|
||||||
|
env=env,
|
||||||
|
shell=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def remove_asymmetric_key():
|
def remove_asymmetric_key(runner, gnupg):
|
||||||
"""Remove asymmetric key"""
|
"""Remove asymmetric key"""
|
||||||
os.system(
|
env = os.environ.copy()
|
||||||
f'gpg --batch --yes '
|
env['GNUPGHOME'] = gnupg.home
|
||||||
f'--delete-secret-keys {pipes.quote(KEY_FINGERPRINT)}')
|
runner(
|
||||||
os.system(f'gpg --batch --yes --delete-key {pipes.quote(KEY_FINGERPRINT)}')
|
[
|
||||||
|
'gpg', '--batch', '--yes',
|
||||||
|
'--delete-secret-keys', pipes.quote(KEY_FINGERPRINT)
|
||||||
|
],
|
||||||
|
env=env,
|
||||||
|
shell=True,
|
||||||
|
)
|
||||||
|
runner(
|
||||||
|
[
|
||||||
|
'gpg', '--batch', '--yes',
|
||||||
|
'--delete-key', pipes.quote(KEY_FINGERPRINT)
|
||||||
|
],
|
||||||
|
env=env,
|
||||||
|
shell=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def asymmetric_key():
|
def asymmetric_key(runner, gnupg):
|
||||||
"""Fixture for asymmetric key, removed in teardown"""
|
"""Fixture for asymmetric key, removed in teardown"""
|
||||||
add_asymmetric_key()
|
add_asymmetric_key(runner, gnupg)
|
||||||
yield KEY_NAME
|
yield KEY_NAME
|
||||||
remove_asymmetric_key()
|
remove_asymmetric_key(runner, gnupg)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -95,7 +120,7 @@ def encrypt_targets(yadm_y, paths):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
def decrypt_targets(tmpdir_factory, runner):
|
def decrypt_targets(tmpdir_factory, runner, gnupg):
|
||||||
"""Fixture for setting data to decrypt
|
"""Fixture for setting data to decrypt
|
||||||
|
|
||||||
This fixture:
|
This fixture:
|
||||||
|
@ -117,17 +142,21 @@ def decrypt_targets(tmpdir_factory, runner):
|
||||||
tmpdir.join('subdir/decrypt3').write('subdir/decrypt3')
|
tmpdir.join('subdir/decrypt3').write('subdir/decrypt3')
|
||||||
expected.append('subdir/decrypt3')
|
expected.append('subdir/decrypt3')
|
||||||
|
|
||||||
|
gnupg.pw(PASSPHRASE)
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['GNUPGHOME'] = gnupg.home
|
||||||
run = runner(
|
run = runner(
|
||||||
['tar', 'cvf', '-'] +
|
['tar', 'cvf', '-'] +
|
||||||
expected +
|
expected +
|
||||||
['|', 'gpg', '--batch', '--yes', '-c'] +
|
['|', 'gpg', '--batch', '--yes', '-c'] +
|
||||||
['--passphrase', pipes.quote(PASSPHRASE)] +
|
|
||||||
['--output', pipes.quote(str(symmetric))],
|
['--output', pipes.quote(str(symmetric))],
|
||||||
cwd=tmpdir,
|
cwd=tmpdir,
|
||||||
|
env=env,
|
||||||
shell=True)
|
shell=True)
|
||||||
assert run.success
|
assert run.success
|
||||||
|
|
||||||
add_asymmetric_key()
|
gnupg.pw('')
|
||||||
|
add_asymmetric_key(runner, gnupg)
|
||||||
run = runner(
|
run = runner(
|
||||||
['tar', 'cvf', '-'] +
|
['tar', 'cvf', '-'] +
|
||||||
expected +
|
expected +
|
||||||
|
@ -135,9 +164,10 @@ def decrypt_targets(tmpdir_factory, runner):
|
||||||
['-r', pipes.quote(KEY_NAME)] +
|
['-r', pipes.quote(KEY_NAME)] +
|
||||||
['--output', pipes.quote(str(asymmetric))],
|
['--output', pipes.quote(str(asymmetric))],
|
||||||
cwd=tmpdir,
|
cwd=tmpdir,
|
||||||
|
env=env,
|
||||||
shell=True)
|
shell=True)
|
||||||
assert run.success
|
assert run.success
|
||||||
remove_asymmetric_key()
|
remove_asymmetric_key(runner, gnupg)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'asymmetric': asymmetric,
|
'asymmetric': asymmetric,
|
||||||
|
@ -147,8 +177,8 @@ def decrypt_targets(tmpdir_factory, runner):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'mismatched_phrase', [False, True],
|
'bad_phrase', [False, True],
|
||||||
ids=['matching_phrase', 'mismatched_phrase'])
|
ids=['good_phrase', 'bad_phrase'])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'missing_encrypt', [False, True],
|
'missing_encrypt', [False, True],
|
||||||
ids=['encrypt_exists', 'encrypt_missing'])
|
ids=['encrypt_exists', 'encrypt_missing'])
|
||||||
|
@ -157,25 +187,25 @@ def decrypt_targets(tmpdir_factory, runner):
|
||||||
ids=['clean', 'overwrite'])
|
ids=['clean', 'overwrite'])
|
||||||
def test_symmetric_encrypt(
|
def test_symmetric_encrypt(
|
||||||
runner, yadm_y, paths, encrypt_targets,
|
runner, yadm_y, paths, encrypt_targets,
|
||||||
overwrite, missing_encrypt, mismatched_phrase):
|
gnupg, bad_phrase, overwrite, missing_encrypt):
|
||||||
"""Test symmetric encryption"""
|
"""Test symmetric encryption"""
|
||||||
|
|
||||||
if missing_encrypt:
|
if missing_encrypt:
|
||||||
paths.encrypt.remove()
|
paths.encrypt.remove()
|
||||||
|
|
||||||
matched_phrase = PASSPHRASE
|
if bad_phrase:
|
||||||
if mismatched_phrase:
|
gnupg.pw('')
|
||||||
matched_phrase = 'mismatched'
|
else:
|
||||||
|
gnupg.pw(PASSPHRASE)
|
||||||
|
|
||||||
if overwrite:
|
if overwrite:
|
||||||
paths.archive.write('existing archive')
|
paths.archive.write('existing archive')
|
||||||
|
|
||||||
run = runner(yadm_y('encrypt'), expect=[
|
env = os.environ.copy()
|
||||||
('passphrase:', PASSPHRASE),
|
env['GNUPGHOME'] = gnupg.home
|
||||||
('passphrase:', matched_phrase),
|
run = runner(yadm_y('encrypt'), env=env)
|
||||||
])
|
|
||||||
|
|
||||||
if missing_encrypt or mismatched_phrase:
|
if missing_encrypt or bad_phrase:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
else:
|
else:
|
||||||
assert run.success
|
assert run.success
|
||||||
|
@ -183,15 +213,16 @@ def test_symmetric_encrypt(
|
||||||
|
|
||||||
if missing_encrypt:
|
if missing_encrypt:
|
||||||
assert 'does not exist' in run.out
|
assert 'does not exist' in run.out
|
||||||
elif mismatched_phrase:
|
elif bad_phrase:
|
||||||
assert 'invalid passphrase' in run.out
|
assert 'Invalid passphrase' in run.err
|
||||||
else:
|
else:
|
||||||
assert encrypted_data_valid(runner, paths.archive, encrypt_targets)
|
assert encrypted_data_valid(
|
||||||
|
runner, gnupg, paths.archive, encrypt_targets)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'wrong_phrase', [False, True],
|
'bad_phrase', [False, True],
|
||||||
ids=['correct_phrase', 'wrong_phrase'])
|
ids=['good_phrase', 'bad_phrase'])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'archive_exists', [True, False],
|
'archive_exists', [True, False],
|
||||||
ids=['archive_exists', 'archive_missing'])
|
ids=['archive_exists', 'archive_missing'])
|
||||||
|
@ -199,16 +230,18 @@ def test_symmetric_encrypt(
|
||||||
'dolist', [False, True],
|
'dolist', [False, True],
|
||||||
ids=['decrypt', 'list'])
|
ids=['decrypt', 'list'])
|
||||||
def test_symmetric_decrypt(
|
def test_symmetric_decrypt(
|
||||||
runner, yadm_y, paths, decrypt_targets,
|
runner, yadm_y, paths, decrypt_targets, gnupg,
|
||||||
dolist, archive_exists, wrong_phrase):
|
dolist, archive_exists, bad_phrase):
|
||||||
"""Test decryption"""
|
"""Test decryption"""
|
||||||
|
|
||||||
# init empty yadm repo
|
# init empty yadm repo
|
||||||
os.system(' '.join(yadm_y('init', '-w', str(paths.work), '-f')))
|
os.system(' '.join(yadm_y('init', '-w', str(paths.work), '-f')))
|
||||||
|
|
||||||
phrase = PASSPHRASE
|
if bad_phrase:
|
||||||
if wrong_phrase:
|
gnupg.pw('')
|
||||||
phrase = 'wrong-phrase'
|
time.sleep(1) # allow gpg-agent cache to expire
|
||||||
|
else:
|
||||||
|
gnupg.pw(PASSPHRASE)
|
||||||
|
|
||||||
if archive_exists:
|
if archive_exists:
|
||||||
decrypt_targets['symmetric'].copy(paths.archive)
|
decrypt_targets['symmetric'].copy(paths.archive)
|
||||||
|
@ -216,15 +249,18 @@ def test_symmetric_decrypt(
|
||||||
# to test overwriting
|
# to test overwriting
|
||||||
paths.work.join('decrypt1').write('pre-existing file')
|
paths.work.join('decrypt1').write('pre-existing file')
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
|
||||||
args = []
|
args = []
|
||||||
|
|
||||||
if dolist:
|
if dolist:
|
||||||
args.append('-l')
|
args.append('-l')
|
||||||
run = runner(yadm_y('decrypt') + args, expect=[('passphrase:', phrase)])
|
run = runner(yadm_y('decrypt') + args, env=env)
|
||||||
|
|
||||||
if archive_exists and not wrong_phrase:
|
if archive_exists and not bad_phrase:
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ''
|
assert 'encrypted with 1 passphrase' in run.err
|
||||||
if dolist:
|
if dolist:
|
||||||
for filename in decrypt_targets['expected']:
|
for filename in decrypt_targets['expected']:
|
||||||
if filename != 'decrypt1': # this one should exist
|
if filename != 'decrypt1': # this one should exist
|
||||||
|
@ -248,7 +284,7 @@ def test_symmetric_decrypt(
|
||||||
'overwrite', [False, True],
|
'overwrite', [False, True],
|
||||||
ids=['clean', 'overwrite'])
|
ids=['clean', 'overwrite'])
|
||||||
def test_asymmetric_encrypt(
|
def test_asymmetric_encrypt(
|
||||||
runner, yadm_y, paths, encrypt_targets,
|
runner, yadm_y, paths, encrypt_targets, gnupg,
|
||||||
overwrite, key_exists, ask):
|
overwrite, key_exists, ask):
|
||||||
"""Test asymmetric encryption"""
|
"""Test asymmetric encryption"""
|
||||||
|
|
||||||
|
@ -264,13 +300,17 @@ def test_asymmetric_encrypt(
|
||||||
paths.archive.write('existing archive')
|
paths.archive.write('existing archive')
|
||||||
|
|
||||||
if not key_exists:
|
if not key_exists:
|
||||||
remove_asymmetric_key()
|
remove_asymmetric_key(runner, gnupg)
|
||||||
|
|
||||||
run = runner(yadm_y('encrypt'), expect=expect)
|
env = os.environ.copy()
|
||||||
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
|
||||||
|
run = runner(yadm_y('encrypt'), env=env, expect=expect)
|
||||||
|
|
||||||
if key_exists:
|
if key_exists:
|
||||||
assert run.success
|
assert run.success
|
||||||
assert encrypted_data_valid(runner, paths.archive, encrypt_targets)
|
assert encrypted_data_valid(
|
||||||
|
runner, gnupg, paths.archive, encrypt_targets)
|
||||||
else:
|
else:
|
||||||
assert run.failure
|
assert run.failure
|
||||||
assert 'Unable to write' in run.out
|
assert 'Unable to write' in run.out
|
||||||
|
@ -287,7 +327,7 @@ def test_asymmetric_encrypt(
|
||||||
'dolist', [False, True],
|
'dolist', [False, True],
|
||||||
ids=['decrypt', 'list'])
|
ids=['decrypt', 'list'])
|
||||||
def test_asymmetric_decrypt(
|
def test_asymmetric_decrypt(
|
||||||
runner, yadm_y, paths, decrypt_targets,
|
runner, yadm_y, paths, decrypt_targets, gnupg,
|
||||||
dolist, key_exists):
|
dolist, key_exists):
|
||||||
"""Test decryption"""
|
"""Test decryption"""
|
||||||
|
|
||||||
|
@ -300,13 +340,15 @@ def test_asymmetric_decrypt(
|
||||||
paths.work.join('decrypt1').write('pre-existing file')
|
paths.work.join('decrypt1').write('pre-existing file')
|
||||||
|
|
||||||
if not key_exists:
|
if not key_exists:
|
||||||
remove_asymmetric_key()
|
remove_asymmetric_key(runner, gnupg)
|
||||||
|
|
||||||
args = []
|
args = []
|
||||||
|
|
||||||
if dolist:
|
if dolist:
|
||||||
args.append('-l')
|
args.append('-l')
|
||||||
run = runner(yadm_y('decrypt') + args)
|
env = os.environ.copy()
|
||||||
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
run = runner(yadm_y('decrypt') + args, env=env)
|
||||||
|
|
||||||
if key_exists:
|
if key_exists:
|
||||||
assert run.success
|
assert run.success
|
||||||
|
@ -327,7 +369,8 @@ def test_asymmetric_decrypt(
|
||||||
'untracked',
|
'untracked',
|
||||||
[False, 'y', 'n'],
|
[False, 'y', 'n'],
|
||||||
ids=['tracked', 'untracked_answer_y', 'untracked_answer_n'])
|
ids=['tracked', 'untracked_answer_y', 'untracked_answer_n'])
|
||||||
def test_offer_to_add(runner, yadm_y, paths, encrypt_targets, untracked):
|
def test_offer_to_add(
|
||||||
|
runner, yadm_y, paths, encrypt_targets, gnupg, untracked):
|
||||||
"""Test offer to add encrypted archive
|
"""Test offer to add encrypted archive
|
||||||
|
|
||||||
All the other encryption tests use an archive outside of the work tree.
|
All the other encryption tests use an archive outside of the work tree.
|
||||||
|
@ -336,10 +379,12 @@ def test_offer_to_add(runner, yadm_y, paths, encrypt_targets, untracked):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
worktree_archive = paths.work.join('worktree-archive.tar.gpg')
|
worktree_archive = paths.work.join('worktree-archive.tar.gpg')
|
||||||
expect = [
|
|
||||||
('passphrase:', PASSPHRASE),
|
expect = []
|
||||||
('passphrase:', PASSPHRASE),
|
|
||||||
]
|
gnupg.pw(PASSPHRASE)
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['GNUPGHOME'] = gnupg.home
|
||||||
|
|
||||||
if untracked:
|
if untracked:
|
||||||
expect.append(('add it now', untracked))
|
expect.append(('add it now', untracked))
|
||||||
|
@ -349,12 +394,14 @@ def test_offer_to_add(runner, yadm_y, paths, encrypt_targets, untracked):
|
||||||
|
|
||||||
run = runner(
|
run = runner(
|
||||||
yadm_y('encrypt', '--yadm-archive', str(worktree_archive)),
|
yadm_y('encrypt', '--yadm-archive', str(worktree_archive)),
|
||||||
|
env=env,
|
||||||
expect=expect
|
expect=expect
|
||||||
)
|
)
|
||||||
|
|
||||||
assert run.success
|
assert run.success
|
||||||
assert run.err == ''
|
assert run.err == ''
|
||||||
assert encrypted_data_valid(runner, worktree_archive, encrypt_targets)
|
assert encrypted_data_valid(
|
||||||
|
runner, gnupg, worktree_archive, encrypt_targets)
|
||||||
|
|
||||||
run = runner(
|
run = runner(
|
||||||
yadm_y('status', '--porcelain', '-uall', str(worktree_archive)))
|
yadm_y('status', '--porcelain', '-uall', str(worktree_archive)))
|
||||||
|
@ -372,22 +419,20 @@ def test_offer_to_add(runner, yadm_y, paths, encrypt_targets, untracked):
|
||||||
assert f'AM {worktree_archive.basename}' in run.out
|
assert f'AM {worktree_archive.basename}' in run.out
|
||||||
|
|
||||||
|
|
||||||
def test_encrypt_added_to_exclude(runner, yadm_y, paths):
|
@pytest.mark.usefixtures('ds1_copy')
|
||||||
|
def test_encrypt_added_to_exclude(runner, yadm_y, paths, gnupg):
|
||||||
"""Confirm that .config/yadm/encrypt is added to exclude"""
|
"""Confirm that .config/yadm/encrypt is added to exclude"""
|
||||||
|
|
||||||
expect = [
|
gnupg.pw(PASSPHRASE)
|
||||||
('passphrase:', PASSPHRASE),
|
env = os.environ.copy()
|
||||||
('passphrase:', PASSPHRASE),
|
env['GNUPGHOME'] = gnupg.home
|
||||||
]
|
|
||||||
|
|
||||||
exclude_file = paths.repo.join('info/exclude')
|
exclude_file = paths.repo.join('info/exclude')
|
||||||
paths.encrypt.write('test-encrypt-data\n')
|
paths.encrypt.write('test-encrypt-data\n')
|
||||||
|
paths.work.join('test-encrypt-data').write('')
|
||||||
exclude_file.write('original-data', ensure=True)
|
exclude_file.write('original-data', ensure=True)
|
||||||
|
|
||||||
run = runner(
|
run = runner(yadm_y('encrypt'), env=env)
|
||||||
yadm_y('encrypt'),
|
|
||||||
expect=expect,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert 'test-encrypt-data' in paths.repo.join('info/exclude').read()
|
assert 'test-encrypt-data' in paths.repo.join('info/exclude').read()
|
||||||
assert 'original-data' in paths.repo.join('info/exclude').read()
|
assert 'original-data' in paths.repo.join('info/exclude').read()
|
||||||
|
@ -395,14 +440,16 @@ def test_encrypt_added_to_exclude(runner, yadm_y, paths):
|
||||||
assert run.err == ''
|
assert run.err == ''
|
||||||
|
|
||||||
|
|
||||||
def encrypted_data_valid(runner, encrypted, expected):
|
def encrypted_data_valid(runner, gnupg, encrypted, expected):
|
||||||
"""Verify encrypted data matches expectations"""
|
"""Verify encrypted data matches expectations"""
|
||||||
|
gnupg.pw(PASSPHRASE)
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['GNUPGHOME'] = gnupg.home
|
||||||
run = runner([
|
run = runner([
|
||||||
'gpg',
|
'gpg',
|
||||||
'--passphrase', pipes.quote(PASSPHRASE),
|
|
||||||
'-d', pipes.quote(str(encrypted)),
|
'-d', pipes.quote(str(encrypted)),
|
||||||
'2>/dev/null',
|
'2>/dev/null',
|
||||||
'|', 'tar', 't'], shell=True, report=False)
|
'|', 'tar', 't'], env=env, shell=True, report=False)
|
||||||
file_count = 0
|
file_count = 0
|
||||||
for filename in run.out.splitlines():
|
for filename in run.out.splitlines():
|
||||||
if filename.endswith('/'):
|
if filename.endswith('/'):
|
||||||
|
|
Loading…
Reference in a new issue