def test_verify_signature_bad(self): to_sign = "this is some text.\nIt's something\n." similar = "this is some text.\r\n.It's something\r\n." _, detached = crypto.detached_signature_for(to_sign, self.key) with self.assertRaises(GPGProblem): crypto.verify_detached(similar, detached)
def test_verify_signature_bad(self): to_sign = "this is some text.\nIt's something\n." similar = "this is some text.\r\n.It's something\r\n." with gpg.core.Context() as ctx: _, detached = crypto.detached_signature_for(to_sign, [ctx.get_key(FPR)]) with self.assertRaises(GPGProblem): crypto.verify_detached(similar, detached)
def test_verify_signature_good(self): to_sign = "this is some text.\nIt's something\n." _, detached = crypto.detached_signature_for(to_sign, self.key) try: crypto.verify_detached(to_sign, detached) except GPGProblem: raise AssertionError
def test_verify_signature_good(self): to_sign = "this is some text.\nIt's something\n." with gpg.core.Context() as ctx: _, detached = crypto.detached_signature_for(to_sign, [ctx.get_key(FPR)]) try: crypto.verify_detached(to_sign, detached) except GPGProblem: raise AssertionError
def test_verify_signature_bad(self): to_sign = b"this is some text.\nIt's something\n." similar = b"this is some text.\r\n.It's something\r\n." with gpg.core.Context() as ctx: _, detached = crypto.detached_signature_for( to_sign, [ctx.get_key(FPR)]) with self.assertRaises(GPGProblem): crypto.verify_detached(similar, detached)
def test_verify_signature_good(self): to_sign = b"this is some text.\nIt's something\n." with gpg.core.Context() as ctx: _, detached = crypto.detached_signature_for( to_sign, [ctx.get_key(FPR)]) try: crypto.verify_detached(to_sign, detached) except GPGProblem: raise AssertionError
def message_from_file(handle): '''Reads a mail from the given file-like object and returns an email object, very much like email.message_from_file. In addition to that OpenPGP encrypted data is detected and decrypted. If this succeeds, any mime messages found in the recovered plaintext message are added to the returned message object. :param handle: a file-like object :returns: :class:`email.message.Message` possibly augmented with decrypted data ''' m = email.message_from_file(handle) # make sure noone smuggles a token in (data from m is untrusted) del m[X_SIGNATURE_VALID_HEADER] del m[X_SIGNATURE_MESSAGE_HEADER] p = get_params(m) app_pgp_sig = 'application/pgp-signature' app_pgp_enc = 'application/pgp-encrypted' # handle OpenPGP signed data if (m.is_multipart() and m.get_content_subtype() == 'signed' and p.get('protocol', None) == app_pgp_sig): # RFC 3156 is quite strict: # * exactly two messages # * the second is of type 'application/pgp-signature' # * the second contains the detached signature malformed = False if len(m.get_payload()) != 2: malformed = 'expected exactly two messages, got {0}'.format( len(m.get_payload())) ct = m.get_payload(1).get_content_type() if ct != app_pgp_sig: malformed = 'expected Content-Type: {0}, got: {1}'.format( app_pgp_sig, ct) # TODO: RFC 3156 says the alg has to be lower case, but I've # seen a message with 'PGP-'. maybe we should be more # permissive here, or maybe not, this is crypto stuff... if not p.get('micalg', 'nothing').startswith('pgp-'): malformed = 'expected micalg=pgp-..., got: {0}'.format( p.get('micalg', 'nothing')) sigs = [] if not malformed: try: sigs = crypto.verify_detached(m.get_payload(0).as_string(), m.get_payload(1).get_payload()) except GPGProblem as e: malformed = str(e) add_signature_headers(m, sigs, malformed) # handle OpenPGP encrypted data elif (m.is_multipart() and m.get_content_subtype() == 'encrypted' and p.get('protocol', None) == app_pgp_enc and 'Version: 1' in m.get_payload(0).get_payload()): # RFC 3156 is quite strict: # * exactly two messages # * the first is of type 'application/pgp-encrypted' # * the first contains 'Version: 1' # * the second is of type 'application/octet-stream' # * the second contains the encrypted and possibly signed data malformed = False ct = m.get_payload(0).get_content_type() if ct != app_pgp_enc: malformed = 'expected Content-Type: {0}, got: {1}'.format(app_pgp_enc, ct) want = 'application/octet-stream' ct = m.get_payload(1).get_content_type() if ct != want: malformed = 'expected Content-Type: {0}, got: {1}'.format(want, ct) if not malformed: try: sigs, d = crypto.decrypt_verify(m.get_payload(1).get_payload()) except GPGProblem as e: # signature verification failures end up here too if # the combined method is used, currently this prevents # the interpretation of the recovered plain text # mail. maybe that's a feature. malformed = str(e) else: # parse decrypted message n = message_from_string(d) # add the decrypted message to m. note that n contains # all the attachments, no need to walk over n here. m.attach(n) # add any defects found m.defects.extend(n.defects) # there are two methods for both signed and encrypted # data, one is called 'RFC 1847 Encapsulation' by # RFC 3156, and one is the 'Combined method'. if len(sigs) == 0: # 'RFC 1847 Encapsulation', the signature is a # detached signature found in the recovered mime # message of type multipart/signed. if X_SIGNATURE_VALID_HEADER in n: for k in (X_SIGNATURE_VALID_HEADER, X_SIGNATURE_MESSAGE_HEADER): m[k] = n[k] else: # an encrypted message without signatures # should arouse some suspicion, better warn # the user add_signature_headers(m, [], 'no signature found') else: # 'Combined method', the signatures are returned # by the decrypt_verify function. # note that if we reached this point, we know the # signatures are valid. if they were not valid, # the else block of the current try would not have # been executed add_signature_headers(m, sigs, '') if malformed: msg = 'Malformed OpenPGP message: {0}'.format(malformed) m.attach(email.message_from_string(msg)) return m
def message_from_file(handle): '''Reads a mail from the given file-like object and returns an email object, very much like email.message_from_file. In addition to that OpenPGP encrypted data is detected and decrypted. If this succeeds, any mime messages found in the recovered plaintext message are added to the returned message object. :param handle: a file-like object :returns: :class:`email.message.Message` possibly augmented with decrypted data ''' m = email.message_from_file(handle) # make sure noone smuggles a token in (data from m is untrusted) del m[X_SIGNATURE_VALID_HEADER] del m[X_SIGNATURE_MESSAGE_HEADER] p = get_params(m) app_pgp_sig = 'application/pgp-signature' app_pgp_enc = 'application/pgp-encrypted' # handle OpenPGP signed data if (m.is_multipart() and m.get_content_subtype() == 'signed' and p.get('protocol', None) == app_pgp_sig): # RFC 3156 is quite strict: # * exactly two messages # * the second is of type 'application/pgp-signature' # * the second contains the detached signature malformed = False if len(m.get_payload()) != 2: malformed = u'expected exactly two messages, got {0}'.format( len(m.get_payload())) ct = m.get_payload(1).get_content_type() if ct != app_pgp_sig: malformed = u'expected Content-Type: {0}, got: {1}'.format( app_pgp_sig, ct) # TODO: RFC 3156 says the alg has to be lower case, but I've # seen a message with 'PGP-'. maybe we should be more # permissive here, or maybe not, this is crypto stuff... if not p.get('micalg', 'nothing').startswith('pgp-'): malformed = u'expected micalg=pgp-..., got: {0}'.format( p.get('micalg', 'nothing')) sigs = [] if not malformed: try: sigs = crypto.verify_detached( m.get_payload(0).as_string(), m.get_payload(1).get_payload()) except GPGProblem as e: malformed = unicode(e) add_signature_headers(m, sigs, malformed) # handle OpenPGP encrypted data elif (m.is_multipart() and m.get_content_subtype() == 'encrypted' and p.get('protocol', None) == app_pgp_enc and 'Version: 1' in m.get_payload(0).get_payload()): # RFC 3156 is quite strict: # * exactly two messages # * the first is of type 'application/pgp-encrypted' # * the first contains 'Version: 1' # * the second is of type 'application/octet-stream' # * the second contains the encrypted and possibly signed data malformed = False ct = m.get_payload(0).get_content_type() if ct != app_pgp_enc: malformed = u'expected Content-Type: {0}, got: {1}'.format( app_pgp_enc, ct) want = 'application/octet-stream' ct = m.get_payload(1).get_content_type() if ct != want: malformed = u'expected Content-Type: {0}, got: {1}'.format( want, ct) if not malformed: try: sigs, d = crypto.decrypt_verify(m.get_payload(1).get_payload()) except GPGProblem as e: # signature verification failures end up here too if # the combined method is used, currently this prevents # the interpretation of the recovered plain text # mail. maybe that's a feature. malformed = unicode(e) else: # parse decrypted message n = message_from_string(d) # add the decrypted message to m. note that n contains # all the attachments, no need to walk over n here. m.attach(n) # add any defects found m.defects.extend(n.defects) # there are two methods for both signed and encrypted # data, one is called 'RFC 1847 Encapsulation' by # RFC 3156, and one is the 'Combined method'. if len(sigs) == 0: # 'RFC 1847 Encapsulation', the signature is a # detached signature found in the recovered mime # message of type multipart/signed. if X_SIGNATURE_VALID_HEADER in n: for k in (X_SIGNATURE_VALID_HEADER, X_SIGNATURE_MESSAGE_HEADER): m[k] = n[k] else: # an encrypted message without signatures # should arouse some suspicion, better warn # the user add_signature_headers(m, [], 'no signature found') else: # 'Combined method', the signatures are returned # by the decrypt_verify function. # note that if we reached this point, we know the # signatures are valid. if they were not valid, # the else block of the current try would not have # been executed add_signature_headers(m, sigs, '') if malformed: msg = u'Malformed OpenPGP message: {0}'.format(malformed) content = email.message_from_string(msg.encode('utf-8')) content.set_charset('utf-8') m.attach(content) return m