def test_signed_correct_from(self): """Test that the signature is valid.""" m = self._make_signed() m = utils.decrypted_message_from_bytes(m.as_bytes()) # Don't test for valid/invalid since that might change self.assertIn( 'ambig <*****@*****.**>', m[utils.X_SIGNATURE_MESSAGE_HEADER])
def test_plain_mail(self): m = email.mime.text.MIMEText('This is some text', 'plain', 'utf-8') m['Subject'] = 'test' m['From'] = 'me' m['To'] = 'Nobody' message = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertEqual(message.get_payload(), 'This is some text')
def test_encrypted_length(self): # It seems string that we just attach the unsigned message to the end # of the mail, rather than replacing the whole encrypted payload with # it's unencrypted equivalent m = self._make_encrypted() m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertEqual(len(m.get_payload()), 3)
def test_encrypted_unsigned_doesnt_add_signed_headers(self): """Since the message isn't signed, it shouldn't have headers saying that there is a signature. """ m = self._make_encrypted() m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertNotIn(utils.X_SIGNATURE_VALID_HEADER, m) self.assertNotIn(utils.X_SIGNATURE_MESSAGE_HEADER, m)
def test_encrypted_signed_headers(self): """Since the message is signed, it should have headers saying that there is a signature. """ m = self._make_encrypted(True) m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIn(utils.X_SIGNATURE_MESSAGE_HEADER, m) self.assertIn( 'ambig <*****@*****.**>', m[utils.X_SIGNATURE_MESSAGE_HEADER])
def test_signed_in_multipart_mixed(self): """It is valid to encapsulate a multipart/signed payload inside a multipart/mixed payload, verify that works. """ s = self._make_signed() m = email.mime.multipart.MIMEMultipart('mixed', None, [s]) m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertEqual(m[utils.X_SIGNATURE_VALID_HEADER], 'True') self.assertIn(utils.X_SIGNATURE_MESSAGE_HEADER, m)
def test_erase_alot_header_signature_valid(self): """Alot uses special headers for passing certain kinds of information, it's important that information isn't passed in from the original message as a way to trick the user. """ m = email.message.Message() m.add_header(utils.X_SIGNATURE_VALID_HEADER, 'Bad') message = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIs(message.get(utils.X_SIGNATURE_VALID_HEADER), None)
def test_encrypted_unsigned_in_multipart_mixed(self): """It is valid to encapsulate a multipart/encrypted payload inside a multipart/mixed payload, verify that works. """ s = self._make_encrypted() m = email.mime.multipart.MIMEMultipart('mixed', None, [s]) m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIn('This is some text', [n.get_payload() for n in m.walk()]) self.assertNotIn(utils.X_SIGNATURE_VALID_HEADER, m) self.assertNotIn(utils.X_SIGNATURE_MESSAGE_HEADER, m)
def test_signed_in_multipart_mixed_other_mua(self): """It is valid to encapsulate a multipart/signed payload inside a multipart/mixed payload, verify that works. The signature being sensitive to the way the multipart message was assembled (with blank lines between parts, ...), we need to make sure that we're able to validate messages generated by other MUAs as well. """ mb = Path('tests/static/mail/protonmail-signed.eml').read_bytes() m = utils.decrypted_message_from_bytes(mb) self.assertEqual(m[utils.X_SIGNATURE_VALID_HEADER], 'True') self.assertIn(utils.X_SIGNATURE_MESSAGE_HEADER, m)
def test_signed_more_than_two_messages(self): """Per the spec only 2 payloads may be encapsulated inside the multipart/signed payload, while it might be nice to cover more than 2 payloads (Postel's law), it would introduce serious complexity since we would also need to cover those payloads being misordered. Since getting the right number of payloads and getting them in the right order should be fairly easy to implement correctly enforcing that there are only two payloads seems reasonable. """ m = self._make_signed() m.attach(email.mime.text.MIMEText('foo')) m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIn('expected exactly two messages, got 3', m[utils.X_SIGNATURE_MESSAGE_HEADER])
def test_signed_micalg_cap(self): """The micalg parameter should be normalized to lower case. From RFC 3156 § 5 The "micalg" parameter for the "application/pgp-signature" protocol MUST contain exactly one hash-symbol of the format "pgp-<hash- identifier>", where <hash-identifier> identifies the Message Integrity Check (MIC) algorithm used to generate the signature. Hash-symbols are constructed from the text names registered in [1] or according to the mechanism defined in that document by converting the text name to lower case and prefixing it with the four characters "pgp-". The spec is pretty clear that this is supposed to be lower cased. """ m = self._make_signed() m.set_param('micalg', 'PGP-SHA1') m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIn('expected micalg=pgp-', m[utils.X_SIGNATURE_MESSAGE_HEADER])
def test_signed_headers_included(self): """Headers are added to the message.""" m = self._make_signed() m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIn(utils.X_SIGNATURE_VALID_HEADER, m) self.assertIn(utils.X_SIGNATURE_MESSAGE_HEADER, m)
def test_encrypted_wrong_mimetype_second_payload(self): m = self._make_encrypted() m.get_payload(1).set_type('text/plain') m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIn('Malformed OpenPGP message:', m.get_payload(2).get_payload())
def test_signed_valid(self): """Test that the signature is valid.""" m = self._make_signed() m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertEqual(m[utils.X_SIGNATURE_VALID_HEADER], 'True')
def test_encrypted_signed_is_decrypted(self): m = self._make_encrypted(True) m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIn('This is some text', [n.get_payload() for n in m.walk()])
def test_signed_wrong_mimetype_second_payload(self): m = self._make_signed() m.get_payload(1).set_type('text/plain') m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIn('expected Content-Type: ', m[utils.X_SIGNATURE_MESSAGE_HEADER])
def test_encrypted_unsigned_is_decrypted(self): m = self._make_encrypted() m = utils.decrypted_message_from_bytes(m.as_bytes()) # Check using m.walk, since we're not checking for ordering, just # existence. self.assertIn('This is some text', [n.get_payload() for n in m.walk()])
def test_signed_wrong_micalg(self): m = self._make_signed() m.set_param('micalg', 'foo') m = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIn('expected micalg=pgp-...', m[utils.X_SIGNATURE_MESSAGE_HEADER])
def test_erase_alot_header_message(self): m = email.message.Message() m.add_header(utils.X_SIGNATURE_MESSAGE_HEADER, 'Bad') message = utils.decrypted_message_from_bytes(m.as_bytes()) self.assertIs(message.get(utils.X_SIGNATURE_MESSAGE_HEADER), None)