def init_outgoing_and_proto(_): self.outgoing_mail = OutgoingMail(self.fromAddr, self._km, self._config['cert'], self._config['key'], self._config['host'], self._config['port']) self.proto = SMTPFactory(u'*****@*****.**', self._km, self._config['encrypted_only'], self.outgoing_mail).buildProtocol( ('127.0.0.1', 0)) self.dest = User(ADDRESS, 'gateway.leap.se', self.proto, ADDRESS_2)
def test_message_sign(self): """ Test if message is signed with sender key. """ # mock the key fetching self._km._fetch_keys_from_server = Mock( return_value=fail(errors.KeyNotFound())) recipient = User('*****@*****.**', 'gateway.leap.se', self.proto, ADDRESS) self.outgoing_mail = OutgoingMail( self.fromAddr, self._km, self._config['cert'], self._config['key'], self._config['host'], self._config['port']) def check_signed(res): message, _ = res self.assertTrue('Content-Type' in message) self.assertEqual('multipart/signed', message.get_content_type()) self.assertEqual('application/pgp-signature', message.get_param('protocol')) self.assertEqual('pgp-sha512', message.get_param('micalg')) # assert content of message body = (message.get_payload(0) .get_payload(0) .get_payload(decode=True)) self.assertEqual(self.expected_body, body) # assert content of signature self.assertTrue( message.get_payload(1).get_payload().startswith( '-----BEGIN PGP SIGNATURE-----\n'), 'Message does not start with signature header.') self.assertTrue( message.get_payload(1).get_payload().endswith( '-----END PGP SIGNATURE-----\n'), 'Message does not end with signature footer.') return message def verify(message): # replace EOL before verifying (according to rfc3156) fp = StringIO() g = RFC3156CompliantGenerator( fp, mangle_from_=False, maxheaderlen=76) g.flatten(message.get_payload(0)) signed_text = re.sub('\r?\n', '\r\n', fp.getvalue()) def assert_verify(key): self.assertTrue(ADDRESS_2 in key.address, 'Signature could not be verified.') d = self._km.verify( signed_text, ADDRESS_2, openpgp.OpenPGPKey, detached_sig=message.get_payload(1).get_payload()) d.addCallback(assert_verify) return d d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, recipient) d.addCallback(check_signed) d.addCallback(verify) return d
def _create_outgoing_mail(self): return OutgoingMail(str(self._smtp_config.account_email), self._keymanager, self._smtp_config.cert_path, self._smtp_config.cert_path, str(self._smtp_config.remote_smtp_host), int(self._smtp_config.remote_smtp_port))
def init_outgoing_and_proto(_): self.outgoing_mail = OutgoingMail( self.fromAddr, self._km, self._config['cert'], self._config['key'], self._config['host'], self._config['port']) self.proto = SMTPFactory( u'*****@*****.**', self._km, self._config['encrypted_only'], self.outgoing_mail).buildProtocol(('127.0.0.1', 0)) self.dest = User(ADDRESS, 'gateway.leap.se', self.proto, ADDRESS_2)
def setup_smtp_gateway(port, userid, keymanager, smtp_host, smtp_port, smtp_cert, smtp_key, encrypted_only): """ Setup SMTP gateway to run with Twisted. This function sets up the SMTP gateway configuration and the Twisted reactor. :param port: The port in which to run the server. :type port: int :param userid: The user currently logged in :type userid: str :param keymanager: A Key Manager from where to get recipients' public keys. :type keymanager: leap.common.keymanager.KeyManager :param smtp_host: The hostname of the remote SMTP server. :type smtp_host: str :param smtp_port: The port of the remote SMTP server. :type smtp_port: int :param smtp_cert: The client certificate for authentication. :type smtp_cert: str :param smtp_key: The client key for authentication. :type smtp_key: str :param encrypted_only: Whether the SMTP gateway should send unencrypted mail or not. :type encrypted_only: bool :returns: tuple of SMTPFactory, twisted.internet.tcp.Port """ # configure the use of this service with twistd outgoing_mail = OutgoingMail(userid, keymanager, smtp_cert, smtp_key, smtp_host, smtp_port) factory = SMTPFactory(userid, keymanager, encrypted_only, outgoing_mail) try: tport = reactor.listenTCP(port, factory, interface="localhost") emit(catalog.SMTP_SERVICE_STARTED, str(port)) return factory, tport except CannotListenError: logger.error("STMP Service failed to start: " "cannot listen in port %s" % port) emit(catalog.SMTP_SERVICE_FAILED_TO_START, str(port)) except Exception as exc: logger.error("Unhandled error while launching smtp gateway service") logger.exception(exc)
class TestOutgoingMail(TestCaseWithKeyManager): EMAIL_DATA = [ 'HELO gateway.leap.se', 'MAIL FROM: <%s>' % ADDRESS_2, 'RCPT TO: <%s>' % ADDRESS, 'DATA', 'From: User <%s>' % ADDRESS_2, 'To: Leap <%s>' % ADDRESS, 'Date: ' + datetime.now().strftime('%c'), 'Subject: test message', '', 'This is a secret message.', 'Yours,', 'A.', '', '.', 'QUIT' ] def setUp(self): self.lines = [line for line in self.EMAIL_DATA[4:12]] self.lines.append('') # add a trailing newline self.raw = '\r\n'.join(self.lines) self.expected_body = '\r\n'.join(self.EMAIL_DATA[9:12]) + "\r\n" self.fromAddr = ADDRESS_2 def init_outgoing_and_proto(_): self.outgoing_mail = OutgoingMail(self.fromAddr, self._km, self._config['cert'], self._config['key'], self._config['host'], self._config['port']) self.proto = SMTPFactory(u'*****@*****.**', self._km, self._config['encrypted_only'], self.outgoing_mail).buildProtocol( ('127.0.0.1', 0)) self.dest = User(ADDRESS, 'gateway.leap.se', self.proto, ADDRESS_2) d = TestCaseWithKeyManager.setUp(self) d.addCallback(init_outgoing_and_proto) return d def test_message_encrypt(self): """ Test if message gets encrypted to destination email. """ def check_decryption(res): decrypted, _ = res self.assertEqual('\n' + self.expected_body, decrypted, 'Decrypted text differs from plaintext.') d = self._set_sign_used(ADDRESS) d.addCallback(lambda _: self.outgoing_mail._maybe_encrypt_and_sign( self.raw, self.dest)) d.addCallback(self._assert_encrypted) d.addCallback(lambda message: self._km.decrypt( message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey)) d.addCallback(check_decryption) return d def test_message_encrypt_sign(self): """ Test if message gets encrypted to destination email and signed with sender key. '""" def check_decryption_and_verify(res): decrypted, signkey = res self.assertEqual('\n' + self.expected_body, decrypted, 'Decrypted text differs from plaintext.') self.assertTrue(ADDRESS_2 in signkey.address, "Verification failed") d = self._set_sign_used(ADDRESS) d.addCallback(lambda _: self.outgoing_mail._maybe_encrypt_and_sign( self.raw, self.dest)) d.addCallback(self._assert_encrypted) d.addCallback(lambda message: self._km.decrypt(message.get_payload(1). get_payload(), ADDRESS, openpgp.OpenPGPKey, verify=ADDRESS_2)) d.addCallback(check_decryption_and_verify) return d def test_message_sign(self): """ Test if message is signed with sender key. """ # mock the key fetching self._km._fetch_keys_from_server = Mock( return_value=fail(errors.KeyNotFound())) recipient = User('*****@*****.**', 'gateway.leap.se', self.proto, ADDRESS) self.outgoing_mail = OutgoingMail(self.fromAddr, self._km, self._config['cert'], self._config['key'], self._config['host'], self._config['port']) def check_signed(res): message, _ = res self.assertTrue('Content-Type' in message) self.assertEqual('multipart/signed', message.get_content_type()) self.assertEqual('application/pgp-signature', message.get_param('protocol')) self.assertEqual('pgp-sha512', message.get_param('micalg')) # assert content of message body = (message.get_payload(0).get_payload(0).get_payload( decode=True)) self.assertEqual(self.expected_body, body) # assert content of signature self.assertTrue( message.get_payload(1).get_payload().startswith( '-----BEGIN PGP SIGNATURE-----\n'), 'Message does not start with signature header.') self.assertTrue( message.get_payload(1).get_payload().endswith( '-----END PGP SIGNATURE-----\n'), 'Message does not end with signature footer.') return message def verify(message): # replace EOL before verifying (according to rfc3156) fp = StringIO() g = RFC3156CompliantGenerator(fp, mangle_from_=False, maxheaderlen=76) g.flatten(message.get_payload(0)) signed_text = re.sub('\r?\n', '\r\n', fp.getvalue()) def assert_verify(key): self.assertTrue(ADDRESS_2 in key.address, 'Signature could not be verified.') d = self._km.verify( signed_text, ADDRESS_2, openpgp.OpenPGPKey, detached_sig=message.get_payload(1).get_payload()) d.addCallback(assert_verify) return d d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, recipient) d.addCallback(check_signed) d.addCallback(verify) return d def test_attach_key(self): d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest) d.addCallback(self._assert_encrypted) d.addCallback(self._check_headers, self.lines[:4]) d.addCallback(lambda message: self._km.decrypt( message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey)) d.addCallback(lambda (decrypted, _): self._check_key_attachment(Parser( ).parsestr(decrypted))) return d def test_attach_key_not_known(self): address = "*****@*****.**" lines = self.lines lines[1] = "To: <%s>" % (address, ) raw = '\r\n'.join(lines) dest = User(address, 'gateway.leap.se', self.proto, ADDRESS_2) d = self.outgoing_mail._maybe_encrypt_and_sign(raw, dest) d.addCallback(lambda (message, _): self._check_headers(message, lines[:4])) d.addCallback(self._check_key_attachment) return d def _check_headers(self, message, headers): msgstr = message.as_string(unixfrom=False) for header in headers: self.assertTrue(header in msgstr, "Missing header: %s" % (header, )) return message def _check_key_attachment(self, message): for payload in message.get_payload(): if payload.is_multipart(): return self._check_key_attachment(payload) if 'application/pgp-keys' == payload.get_content_type(): keylines = PUBLIC_KEY_2.split('\n') key = BEGIN_PUBLIC_KEY + '\n\n' + '\n'.join(keylines[4:-1]) self.assertTrue(key in payload.get_payload(decode=True), "Key attachment don't match") return self.fail("No public key attachment found") def _set_sign_used(self, address): def set_sign(key): key.sign_used = True return self._km.put_key(key, address) d = self._km.get_key(address, openpgp.OpenPGPKey, fetch_remote=False) d.addCallback(set_sign) return d def _assert_encrypted(self, res): message, _ = res self.assertTrue('Content-Type' in message) self.assertEqual('multipart/encrypted', message.get_content_type()) self.assertEqual('application/pgp-encrypted', message.get_param('protocol')) self.assertEqual(2, len(message.get_payload())) self.assertEqual('application/pgp-encrypted', message.get_payload(0).get_content_type()) self.assertEqual('application/octet-stream', message.get_payload(1).get_content_type()) return message
def test_message_sign(self): """ Test if message is signed with sender key. """ # mock the key fetching self._km._fetch_keys_from_server = Mock( return_value=fail(errors.KeyNotFound())) recipient = User('*****@*****.**', 'gateway.leap.se', self.proto, ADDRESS) self.outgoing_mail = OutgoingMail(self.fromAddr, self._km, self._config['cert'], self._config['key'], self._config['host'], self._config['port']) def check_signed(res): message, _ = res self.assertTrue('Content-Type' in message) self.assertEqual('multipart/signed', message.get_content_type()) self.assertEqual('application/pgp-signature', message.get_param('protocol')) self.assertEqual('pgp-sha512', message.get_param('micalg')) # assert content of message body = (message.get_payload(0).get_payload(0).get_payload( decode=True)) self.assertEqual(self.expected_body, body) # assert content of signature self.assertTrue( message.get_payload(1).get_payload().startswith( '-----BEGIN PGP SIGNATURE-----\n'), 'Message does not start with signature header.') self.assertTrue( message.get_payload(1).get_payload().endswith( '-----END PGP SIGNATURE-----\n'), 'Message does not end with signature footer.') return message def verify(message): # replace EOL before verifying (according to rfc3156) fp = StringIO() g = RFC3156CompliantGenerator(fp, mangle_from_=False, maxheaderlen=76) g.flatten(message.get_payload(0)) signed_text = re.sub('\r?\n', '\r\n', fp.getvalue()) def assert_verify(key): self.assertTrue(ADDRESS_2 in key.address, 'Signature could not be verified.') d = self._km.verify( signed_text, ADDRESS_2, openpgp.OpenPGPKey, detached_sig=message.get_payload(1).get_payload()) d.addCallback(assert_verify) return d d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, recipient) d.addCallback(check_signed) d.addCallback(verify) return d
class TestOutgoingMail(TestCaseWithKeyManager): EMAIL_DATA = ['HELO gateway.leap.se', 'MAIL FROM: <%s>' % ADDRESS_2, 'RCPT TO: <%s>' % ADDRESS, 'DATA', 'From: User <%s>' % ADDRESS_2, 'To: Leap <%s>' % ADDRESS, 'Date: ' + datetime.now().strftime('%c'), 'Subject: test message', '', 'This is a secret message.', 'Yours,', 'A.', '', '.', 'QUIT'] def setUp(self): self.lines = [line for line in self.EMAIL_DATA[4:12]] self.lines.append('') # add a trailing newline self.raw = '\r\n'.join(self.lines) self.expected_body = '\r\n'.join(self.EMAIL_DATA[9:12]) + "\r\n" self.fromAddr = ADDRESS_2 def init_outgoing_and_proto(_): self.outgoing_mail = OutgoingMail( self.fromAddr, self._km, self._config['cert'], self._config['key'], self._config['host'], self._config['port']) self.proto = SMTPFactory( u'*****@*****.**', self._km, self._config['encrypted_only'], self.outgoing_mail).buildProtocol(('127.0.0.1', 0)) self.dest = User(ADDRESS, 'gateway.leap.se', self.proto, ADDRESS_2) d = TestCaseWithKeyManager.setUp(self) d.addCallback(init_outgoing_and_proto) return d def test_message_encrypt(self): """ Test if message gets encrypted to destination email. """ def check_decryption(res): decrypted, _ = res self.assertEqual( '\n' + self.expected_body, decrypted, 'Decrypted text differs from plaintext.') d = self._set_sign_used(ADDRESS) d.addCallback( lambda _: self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest)) d.addCallback(self._assert_encrypted) d.addCallback(lambda message: self._km.decrypt( message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey)) d.addCallback(check_decryption) return d def test_message_encrypt_sign(self): """ Test if message gets encrypted to destination email and signed with sender key. '""" def check_decryption_and_verify(res): decrypted, signkey = res self.assertEqual( '\n' + self.expected_body, decrypted, 'Decrypted text differs from plaintext.') self.assertTrue(ADDRESS_2 in signkey.address, "Verification failed") d = self._set_sign_used(ADDRESS) d.addCallback( lambda _: self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest)) d.addCallback(self._assert_encrypted) d.addCallback(lambda message: self._km.decrypt( message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey, verify=ADDRESS_2)) d.addCallback(check_decryption_and_verify) return d def test_message_sign(self): """ Test if message is signed with sender key. """ # mock the key fetching self._km._fetch_keys_from_server = Mock( return_value=fail(errors.KeyNotFound())) recipient = User('*****@*****.**', 'gateway.leap.se', self.proto, ADDRESS) self.outgoing_mail = OutgoingMail( self.fromAddr, self._km, self._config['cert'], self._config['key'], self._config['host'], self._config['port']) def check_signed(res): message, _ = res self.assertTrue('Content-Type' in message) self.assertEqual('multipart/signed', message.get_content_type()) self.assertEqual('application/pgp-signature', message.get_param('protocol')) self.assertEqual('pgp-sha512', message.get_param('micalg')) # assert content of message body = (message.get_payload(0) .get_payload(0) .get_payload(decode=True)) self.assertEqual(self.expected_body, body) # assert content of signature self.assertTrue( message.get_payload(1).get_payload().startswith( '-----BEGIN PGP SIGNATURE-----\n'), 'Message does not start with signature header.') self.assertTrue( message.get_payload(1).get_payload().endswith( '-----END PGP SIGNATURE-----\n'), 'Message does not end with signature footer.') return message def verify(message): # replace EOL before verifying (according to rfc3156) fp = StringIO() g = RFC3156CompliantGenerator( fp, mangle_from_=False, maxheaderlen=76) g.flatten(message.get_payload(0)) signed_text = re.sub('\r?\n', '\r\n', fp.getvalue()) def assert_verify(key): self.assertTrue(ADDRESS_2 in key.address, 'Signature could not be verified.') d = self._km.verify( signed_text, ADDRESS_2, openpgp.OpenPGPKey, detached_sig=message.get_payload(1).get_payload()) d.addCallback(assert_verify) return d d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, recipient) d.addCallback(check_signed) d.addCallback(verify) return d def test_attach_key(self): d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest) d.addCallback(self._assert_encrypted) d.addCallback(self._check_headers, self.lines[:4]) d.addCallback(lambda message: self._km.decrypt( message.get_payload(1).get_payload(), ADDRESS, openpgp.OpenPGPKey)) d.addCallback(lambda (decrypted, _): self._check_key_attachment(Parser().parsestr(decrypted))) return d def test_attach_key_not_known(self): address = "*****@*****.**" lines = self.lines lines[1] = "To: <%s>" % (address,) raw = '\r\n'.join(lines) dest = User(address, 'gateway.leap.se', self.proto, ADDRESS_2) d = self.outgoing_mail._maybe_encrypt_and_sign(raw, dest) d.addCallback(lambda (message, _): self._check_headers(message, lines[:4])) d.addCallback(self._check_key_attachment) return d def _check_headers(self, message, headers): msgstr = message.as_string(unixfrom=False) for header in headers: self.assertTrue(header in msgstr, "Missing header: %s" % (header,)) return message def _check_key_attachment(self, message): for payload in message.get_payload(): if payload.is_multipart(): return self._check_key_attachment(payload) if 'application/pgp-keys' == payload.get_content_type(): keylines = PUBLIC_KEY_2.split('\n') key = BEGIN_PUBLIC_KEY + '\n\n' + '\n'.join(keylines[4:-1]) self.assertTrue(key in payload.get_payload(decode=True), "Key attachment don't match") return self.fail("No public key attachment found") def _set_sign_used(self, address): def set_sign(key): key.sign_used = True return self._km.put_key(key, address) d = self._km.get_key(address, openpgp.OpenPGPKey, fetch_remote=False) d.addCallback(set_sign) return d def _assert_encrypted(self, res): message, _ = res self.assertTrue('Content-Type' in message) self.assertEqual('multipart/encrypted', message.get_content_type()) self.assertEqual('application/pgp-encrypted', message.get_param('protocol')) self.assertEqual(2, len(message.get_payload())) self.assertEqual('application/pgp-encrypted', message.get_payload(0).get_content_type()) self.assertEqual('application/octet-stream', message.get_payload(1).get_content_type()) return message