Skip to content
Snippets Groups Projects

pgpy instead of python-gnupg

Open sand requested to merge pgpy into master
Files
6
import os
import shutil
import tempfile
import unittest
import smtplib
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import pgpy
import pytest
import pgp_mime_lib
import logging
logging.getLogger('gnupg').setLevel(logging.DEBUG)
logging.basicConfig()
fixtures_dir = os.path.join(os.path.dirname(__file__), 'fixtures')
secret_key_dir = os.path.join(fixtures_dir, "gnupghome/private-keys-v1.d")
class TestBase(unittest.TestCase):
def setUp(self):
self.dir = tempfile.mkdtemp()
# Load our test secret key.
self.public_keyring = os.path.join(
fixtures_dir, 'gnupghome/pubring.gpg')
self.secret_key_dir = secret_key_dir
self.secret_key = '0x56A1E35992BCB5CF'
def tearDown(self):
shutil.rmtree(self.dir)
def _gpg(self):
return pgp_mime_lib.GPG(self.secret_key, self.public_keyring, self.secret_key_dir)
class TestSign(TestBase):
def test_sign_ok(self):
result = self._gpg().sign_message(MIMEText('test message'))
self.assertTrue(result)
class TestEncrypt(TestBase):
def test_encrypt_and_sign_ok(self):
with open(os.path.join(fixtures_dir, 'pubkey1.asc')) as fd:
pubkey = fd.read()
msg = MIMEText('test message')
result = self._gpg().sign_and_encrypt_message(msg, pubkey)
self.assertTrue(result)
def test_encrypt_and_sign_ok_v2key(self):
with open(os.path.join(fixtures_dir, 'pubkey2.asc')) as fd:
pubkey = fd.read()
msg = MIMEText('test message')
result = self._gpg().sign_and_encrypt_message(msg, pubkey)
self.assertTrue(result)
def test_encrypt_fails_with_revoked_public_key(self):
with open(os.path.join(fixtures_dir, 'revoked.asc')) as fd:
pubkey = fd.read()
self.assertRaises(
pgp_mime_lib.Error,
self._gpg().encrypt_message, MIMEText('test_message'), pubkey)
def test_encrypt_fails_with_more_than_one_public_key(self):
with open(os.path.join(fixtures_dir, 'twokeys.asc')) as fd:
pubkey = fd.read()
self.assertRaises(
pgp_mime_lib.Error,
self._gpg().encrypt_message, MIMEText('test_message'), pubkey)
def test_encrypt_fails_with_secret_key(self):
# People will do this!
with open(os.path.join(fixtures_dir, 'secretkey.asc')) as fd:
pubkey = fd.read()
self.assertRaises(
pgp_mime_lib.Error,
self._gpg().encrypt_message, MIMEText('test_message'), pubkey)
def test_encrypt_fails_with_bad_public_key(self):
self.assertRaises(
pgp_mime_lib.Error,
self._gpg().encrypt_message, MIMEText('test_message'),
'this is not a pgp key')
def test_encrypt_fails_with_our_own_public_key(self):
# Being fed our own public key is pretty much the only way to
# get a 0 imported keys result from gpg.
with open(os.path.join(fixtures_dir, '92BCB5CF.asc')) as fd:
pubkey = fd.read()
self.assertRaises(
pgp_mime_lib.Error,
self._gpg().encrypt_message, MIMEText('test_message'),
pubkey)
def test_encrypt_and_sign_send_via_smtp(self):
# Send an email to yourself if you want to manually check the
# results, by defining some environment variables.
try:
smtp_server = os.environ['SMTP_SERVER']
smtp_user = os.environ['SMTP_USER']
smtp_password = os.environ['SMTP_PASSWORD']
smtp_recip = os.environ['SMTP_RECIPIENT']
smtp_pubkey = os.environ['SMTP_PUBLIC_KEY']
except KeyError:
raise unittest.SkipTest()
with open(smtp_pubkey) as fd:
pubkey = fd.read()
msg = MIMEText('test message')
result = self._gpg().sign_and_encrypt_message(msg, pubkey)
result['To'] = smtp_recip
result['Subject'] = 'test output'
s = smtplib.SMTP_SSL(smtp_server, 465)
s.login(smtp_user, smtp_password)
s.sendmail(smtp_recip, [smtp_recip], result.as_string(unixfrom=False))
s.quit()
class TestParsePublicKey(TestBase):
def test_parse_public_key_ok(self):
with open(os.path.join(fixtures_dir, 'pubkey1.asc')) as fd:
pubkey = fd.read()
result = pgp_mime_lib.parse_public_key(pubkey)
self.assertTrue(result)
def test_parse_public_key_fails_with_bad_public_key(self):
self.assertRaises(
pgp_mime_lib.Error, pgp_mime_lib.parse_public_key,
'this is not a pgp key')
def test_parse_public_key_fails_with_empty_data(self):
self.assertRaises(
pgp_mime_lib.Error, pgp_mime_lib.parse_public_key,
'')
def test_parse_public_key_fails_with_empty_pgp_key_block(self):
self.assertRaises(
pgp_mime_lib.Error, pgp_mime_lib.parse_public_key,
'''
def load_pubkey(filename):
"""Load a PGP public key from the fixtures directory"""
with open(os.path.join(fixtures_dir, filename), 'rb') as fd:
return fd.read()
@pytest.fixture
def gpg():
"""Provide a GPG instance preloaded with test keys as a fixture"""
public_keyring = os.path.join(fixtures_dir, 'gnupghome/pubring.gpg')
secret_key_file = os.path.join(fixtures_dir, 'gnupghome/secring.gpg')
secret_key = '0x56A1E35992BCB5CF'
return pgp_mime_lib.GPG(secret_key, public_keyring, secret_key_file)
def test_sign_and_verify(gpg):
"""Sign a message with our own private key; verify the signature"""
text = "test message"
result = gpg.sign_message(MIMEText(text))
assert result.is_multipart() is True
cleartext = None
for part in result.walk():
if part.get_content_type() == "text/plain":
cleartext = pgp_mime_lib._openpgp_mangle_for_signature(part)
if part.get_content_type() == "application/pgp-signature":
sig = pgpy.PGPSignature.from_blob(part.get_payload(decode=True))
assert bool(gpg.public_key.verify(cleartext, sig)) is True
def test_encrypt_and_sign_ok(gpg):
"""Sign and encrypt a message; ensure that is encrypted"""
pubkey = load_pubkey("pubkey1.asc")
msg = MIMEText('test message')
result = gpg.sign_and_encrypt_message(msg, pubkey)
assert result.is_multipart() is True
for part in result.walk():
content_type = part.get_content_type()
if content_type == "application/octet-stream":
encrypted = pgpy.PGPMessage.from_blob(part.get_payload(decode=True))
# at least ensure that we can load the message as a valid PGP message
assert encrypted.is_encrypted is True
def test_encrypt_and_sign_ok_v2key(gpg):
"""Sign and encrypt a message with a v2 key; ensure that is encrypted"""
pubkey = load_pubkey('pubkey2.asc')
msg = MIMEText('test message')
result = gpg.sign_and_encrypt_message(msg, pubkey)
assert result.is_multipart() is True
for part in result.walk():
content_type = part.get_content_type()
if content_type == "application/octet-stream":
encrypted = pgpy.PGPMessage.from_blob(part.get_payload(decode=True))
# at least ensure that we can load the message as a valid PGP message
assert encrypted.is_encrypted is True
def test_encrypt_fails_with_revoked_public_key(gpg):
pubkey = load_pubkey('revoked.asc')
with pytest.raises(pgp_mime_lib.Error):
gpg.encrypt_message(MIMEText('test_message'), pubkey)
def test_encrypt_fails_with_more_than_one_public_key(gpg):
pubkey = load_pubkey("twokeys.asc")
with pytest.raises(pgp_mime_lib.Error):
gpg.encrypt_message(MIMEText('test_message'), pubkey)
def test_encrypt_fails_with_secret_key(gpg):
# People will do this!
pubkey = load_pubkey("secretkey.asc")
with pytest.raises(pgp_mime_lib.Error):
gpg.encrypt_message(MIMEText('test_message'), pubkey)
def test_encrypt_fails_with_bad_public_key(gpg):
with pytest.raises(pgp_mime_lib.Error):
gpg.encrypt_message(
MIMEText('test_message'),
'this is not a pgp key'
)
@pytest.mark.skip("the pgpy implementation do not use a keyring at all")
def test_encrypt_fails_with_our_own_public_key(gpg):
# Being fed our own public key is pretty much the only way to
# get a 0 imported keys result from gpg.
pubkey = load_pubkey("92BCB5CF.asc")
with pytest.raises(pgp_mime_lib.Error):
gpg.encrypt_message(
MIMEText('test_message'),
pubkey
)
def test_encrypt_and_sign_send_via_smtp(gpg):
# Send an email to yourself if you want to manually check the
# results, by defining some environment variables.
try:
smtp_server = os.environ['SMTP_SERVER']
smtp_user = os.environ['SMTP_USER']
smtp_password = os.environ['SMTP_PASSWORD']
smtp_recip = os.environ['SMTP_RECIPIENT']
smtp_pubkey = os.environ['SMTP_PUBLIC_KEY']
except KeyError:
pytest.skip("SMTP test not configured.")
with open(smtp_pubkey) as fd:
pubkey = fd.read()
msg = MIMEText('test message')
result = gpg.sign_and_encrypt_message(msg, pubkey)
result['To'] = smtp_recip
result['Subject'] = 'test output'
s = smtplib.SMTP_SSL(smtp_server, 465)
s.login(smtp_user, smtp_password)
s.sendmail(smtp_recip, [smtp_recip], result.as_string(unixfrom=False))
s.quit()
def test_parse_public_key_ok():
pubkey = load_pubkey("pubkey1.asc")
result = pgp_mime_lib.parse_public_key(pubkey)
assert result.is_public is True
def test_parse_public_key_fails_with_bad_public_key():
with pytest.raises(pgp_mime_lib.Error):
pgp_mime_lib.parse_public_key('this is not a pgp key')
def test_parse_public_key_fails_with_empty_data():
with pytest.raises(pgp_mime_lib.Error):
pgp_mime_lib.parse_public_key('')
def test_parse_public_key_fails_with_empty_pgp_key_block():
with pytest.raises(pgp_mime_lib.Error):
pgp_mime_lib.parse_public_key('''
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2
-----END PGP PUBLIC KEY BLOCK-----
''')
def test_parse_public_key_fails_with_revoked_public_key(self):
with open(os.path.join(fixtures_dir, 'revoked.asc')) as fd:
pubkey = fd.read()
self.assertRaises(
pgp_mime_lib.Error, pgp_mime_lib.parse_public_key,
pubkey)
def test_parse_public_key_fails_with_more_than_one_key(self):
with open(os.path.join(fixtures_dir, 'twokeys.asc')) as fd:
pubkey = fd.read()
self.assertRaises(
pgp_mime_lib.Error, pgp_mime_lib.parse_public_key,
pubkey)
def test_parse_public_key_fails_with_secret_key(self):
with open(os.path.join(fixtures_dir, 'secretkey.asc')) as fd:
pubkey = fd.read()
self.assertRaises(
pgp_mime_lib.Error, pgp_mime_lib.parse_public_key,
pubkey)
class TestMIME(TestBase):
def _encrypt(self, msg):
with open(os.path.join(fixtures_dir, 'pubkey1.asc')) as fd:
pubkey = fd.read()
result = self._gpg().sign_and_encrypt_message(msg, pubkey)
self.assertTrue(result)
def test_simple_text_message(self):
self._encrypt(MIMEText('test message'))
def test_base64_text_message(self):
self._encrypt(MIMEText(u'test message', 'plain', 'utf-8'))
def test_8bit_text_message(self):
m = MIMEText(u'test message')
m.replace_header('Content-Transfer-Encoding', '8bit')
self._encrypt(m)
def test_multipart_message_with_image(self):
msg = MIMEMultipart()
msg.preamble = 'This is a message with an image attachment'
msg.attach(MIMEImage('GIF89ZZ', 'gif'))
self._encrypt(msg)
def test_multipart_alternative(self):
msg = MIMEMultipart('alternative')
msg.attach(MIMEText('text version', 'plain'))
msg.attach(MIMEText('html version', 'html'))
self._encrypt(msg)
''')
def test_parse_public_key_fails_with_revoked_public_key():
pubkey = load_pubkey("revoked.asc")
with pytest.raises(pgp_mime_lib.Error):
pgp_mime_lib.parse_public_key(pubkey)
def test_parse_public_key_fails_with_more_than_one_key():
pubkey = load_pubkey("twokeys.asc")
with pytest.raises(pgp_mime_lib.Error):
pgp_mime_lib.parse_public_key(pubkey)
def test_parse_public_key_fails_with_secret_key():
pubkey = load_pubkey("secretkey.asc")
with pytest.raises(pgp_mime_lib.Error):
pgp_mime_lib.parse_public_key(pubkey)
@pytest.fixture
def encrypt_test_msg():
def _encrypt_test_msg(gpg, msg):
pubkey = load_pubkey("pubkey1.asc")
return gpg.sign_and_encrypt_message(msg, pubkey)
return _encrypt_test_msg
def test_simple_text_message(gpg, encrypt_test_msg):
result = encrypt_test_msg(gpg, MIMEText('test message'))
assert result.is_multipart() is True
for part in result.walk():
content_type = part.get_content_type()
if content_type == "application/octet-stream":
encrypted = pgpy.PGPMessage.from_blob(part.get_payload(decode=True))
# at least ensure that we can load the message as a valid PGP message
assert encrypted.is_encrypted is True
def test_base64_text_message(gpg, encrypt_test_msg):
result = encrypt_test_msg(gpg, MIMEText(u'test message', 'plain', 'utf-8'))
assert result.is_multipart() is True
for part in result.walk():
content_type = part.get_content_type()
if content_type == "application/octet-stream":
encrypted = pgpy.PGPMessage.from_blob(part.get_payload(decode=True))
# at least ensure that we can load the message as a valid PGP message
assert encrypted.is_encrypted is True
def test_8bit_text_message(gpg, encrypt_test_msg):
m = MIMEText(u'test message')
m.replace_header('Content-Transfer-Encoding', '8bit')
assert encrypt_test_msg(gpg, m) is not None
def test_multipart_message_with_image(gpg, encrypt_test_msg):
msg = MIMEMultipart()
msg.preamble = 'This is a message with an image attachment'
msg.attach(MIMEImage('GIF89ZZ', 'gif'))
assert encrypt_test_msg(gpg, msg) is not None
def test_multipart_alternative(gpg, encrypt_test_msg):
msg = MIMEMultipart('alternative')
msg.attach(MIMEText('text version', 'plain'))
msg.attach(MIMEText('html version', 'html'))
assert encrypt_test_msg(gpg, msg) is not None
Loading