- Implemented proper parsing of ECDSA keys - closes #109

- Added ECDSA signing and verification - closes #111
 - ECDSA key generation is also supported
 - refactored some of the signing/verification machinery to simplify code in PGPKey._sign and PGPKey.verify
 - fixed a bug with Signature subpacket ByteFlags where the byte output was not being properly null-padded to the length of the subpacket when necessary, which led to the potential for key outputs that would fail to parse properly if update_hlen was not called (which is silly)
 - fixed string formatting UserID packets when the comment and/or email address are missing - closes #142
 - implemented OpaquePubKey and OpaquePrivKey so that keys using unimplemented formats can still be loaded without error, even if they can't be used
 - enabled initial key action testing using ecc.1.pub.asc and ecc.1.sec.asc (which also required copying the two signatures from ecc.1.pub.asc to ecc.1.sec.asc)
 - improved test output for some tests with auto-ids that were formed in an unfortunate manner
This commit is contained in:
Michael Greene
2015-06-10 22:39:21 -07:00
parent f3300d3895
commit c76ba55ee5
14 changed files with 477 additions and 123 deletions

View File

@@ -39,13 +39,18 @@ def _read(f, mode='r'):
return ff.read()
comp_algs = [ CompressionAlgorithm.Uncompressed, CompressionAlgorithm.ZIP, CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2 ]
class TestPGPMessage(object):
params = {
'comp_alg': [ CompressionAlgorithm.Uncompressed, CompressionAlgorithm.ZIP, CompressionAlgorithm.ZLIB,
CompressionAlgorithm.BZ2 ],
'comp_alg': comp_algs,
'enc_msg': [ PGPMessage.from_file(f) for f in glob.glob('tests/testdata/messages/message*.pass*.asc') ],
'file': ['tests/testdata/lit', 'tests/testdata/lit2', 'tests/testdata/lit_de']
}
ids = {
'test_new': [ str(ca).split('.')[-1] for ca in comp_algs ],
}
attrs = {
'tests/testdata/lit':
[('filename', 'lit'),
@@ -190,6 +195,9 @@ def sessionkey():
return b'\x9d[\xc1\x0e\xec\x01k\xbc\xf4\x04UW\xbb\xfb\xb2\xb9'
key_algs = [ PubKeyAlgorithm.RSAEncryptOrSign, PubKeyAlgorithm.DSA, PubKeyAlgorithm.ECDSA ]
class TestPGPKey(object):
params = {
'pub': [ PGPKey.from_file(f)[0] for f in sorted(glob.glob('tests/testdata/keys/*.pub.asc')) ],
@@ -198,7 +206,13 @@ class TestPGPKey(object):
'sigkey': [ PGPKey.from_file(f)[0] for f in sorted(glob.glob('tests/testdata/signatures/*.key.asc')) ],
'sigsig': [ PGPSignature.from_file(f) for f in sorted(glob.glob('tests/testdata/signatures/*.sig.asc')) ],
'sigsubj': sorted(glob.glob('tests/testdata/signatures/*.subj')),
'key_alg': [ PubKeyAlgorithm.RSAEncryptOrSign, PubKeyAlgorithm.DSA ],
'key_alg': key_algs,
}
ids = {
'test_verify_detached': [ os.path.basename(f).replace('.', '_') for f in sorted(glob.glob('tests/testdata/signatures/*.key.asc')) ],
'test_new_key': [ str(ka).split('.')[-1] for ka in key_algs ],
'test_new_subkey': [ str(ka).split('.')[-1] for ka in key_algs ],
'test_gpg_verify_new_key': [ str(ka).split('.')[-1] for ka in key_algs ],
}
string_sigs = dict()
timestamp_sigs = dict()
@@ -259,10 +273,12 @@ class TestPGPKey(object):
assert sig.is_expired
# verify with GnuPG
with write_clean('tests/testdata/string', 'w', string), \
write_clean('tests/testdata/string.asc', 'w', str(sig)), \
gpg_import('./pubtest.asc'):
assert gpg_verify('./string', './string.asc', keyid=sig.signer)
if sig.key_algorithm not in {PubKeyAlgorithm.ECDSA}:
# TODO: cannot test ECDSA against GnuPG as there isn't an easy way of installing v2.1 yet on CI
with write_clean('tests/testdata/string', 'w', string), \
write_clean('tests/testdata/string.asc', 'w', str(sig)), \
gpg_import('./pubtest.asc'):
assert gpg_verify('./string', './string.asc', keyid=sig.signer)
self.string_sigs[sec.fingerprint.keyid] = sig
@@ -288,8 +304,10 @@ class TestPGPKey(object):
ctmessage |= sig
# verify with GnuPG
with write_clean('tests/testdata/ctmessage.asc', 'w', str(ctmessage)), gpg_import('./pubtest.asc'):
assert gpg_verify('./ctmessage.asc', keyid=sig.signer)
if sig.key_algorithm not in {PubKeyAlgorithm.ECDSA}:
# TODO: cannot test ECDSA against GnuPG as there isn't an easy way of installing v2.1 yet on CI
with write_clean('tests/testdata/ctmessage.asc', 'w', str(ctmessage)), gpg_import('./pubtest.asc'):
assert gpg_verify('./ctmessage.asc', keyid=sig.signer)
def test_verify_ctmessage(self, pub, ctmessage):
with self.assert_warnings():
@@ -526,10 +544,14 @@ class TestPGPKey(object):
assert subkey in sv
def test_gpg_verify_new_key(self, key_alg, write_clean, gpg_import, gpg_check_sigs):
# if key_alg in {PubKeyAlgorithm.ECDSA, PubKeyAlgorithm.ECDH}:
# pytest.skip("GnuPG version in use cannot import/verify ")
# with GnuPG
key = self.gen_keys[key_alg]
with write_clean('tests/testdata/genkey.asc', 'w', str(key)), \
gpg_import('./genkey.asc') as kio:
assert 'invalid self-signature' not in kio
assert gpg_check_sigs(key.fingerprint.keyid, *[skid for skid in key._children.keys()])
@@ -564,6 +586,9 @@ class TestPGPKey(object):
pytest.skip("not implemented yet")
def test_revoke_subkey(self, sec, pub, write_clean, gpg_import, gpg_check_sigs):
if sec.key_algorithm == PubKeyAlgorithm.ECDSA:
pytest.skip("ECDH not implemented yet which causes this test to fail")
subkey = next(iter(pub.subkeys.values()))
with self.assert_warnings():
# revoke the first subkey
@@ -571,13 +596,13 @@ class TestPGPKey(object):
assert 'ReasonForRevocation' in rsig._signature.subpackets
subkey |= rsig
# # verify with PGPy
# verify with PGPy
assert pub.verify(subkey)
sv = pub.verify(pub)
assert sv
assert rsig in iter(s.signature for s in sv.good_signatures)
# verify with GPG
# verify with GnuPG
kfp = '{:s}.asc'.format(pub.fingerprint.shortid)
with write_clean(os.path.join('tests', 'testdata', kfp), 'w', str(kfp)), \
gpg_import(os.path.join('.', kfp)) as kio:
@@ -586,7 +611,3 @@ class TestPGPKey(object):
# and remove it, for good measure
subkey._signatures.remove(rsig)
assert rsig not in subkey
# tests where ordering is important