From: Andrew Engelbrecht Date: Fri, 8 Sep 2023 16:05:52 +0000 (-0400) Subject: committing past work on edward X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=97e734761c6d42739ee864507babe10f7015e0b7;p=edward.git committing past work on edward i believe that this code was an attempt to upgrade edward to work with a newer version of python, on newer systems. this code should be reviewed and tested before deployment. --- diff --git a/edward b/edward index d744392..b03fc38 100755 --- a/edward +++ b/edward @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Affero Public License * * along with Edward. If not, see . * * * -* Copyright (C) 2014-2015 Andrew Engelbrecht (AGPLv3+) * +* Copyright (C) 2014-2021 Andrew Engelbrecht (AGPLv3+) * * Copyright (C) 2014 Josh Drake (AGPLv3+) * * Copyright (C) 2014 Lisa Marie Maginnis (AGPLv3+) * * Copyright (C) 2009-2015 Tails developers ( GPLv3+) * @@ -35,8 +35,8 @@ import re import io import os import sys +import gpg import enum -import gpgme import smtplib import importlib @@ -271,12 +271,13 @@ def main (): test_auto_reply(email_bytes) - gpgme_ctx = get_gpg_context(edward_config.gnupghome, - edward_config.sign_with_key) + ctx = gpg.Context(home_dir=edward_config.gnupghome) + signing_keys = list(ctx.get_key(edward_config.sign_with_key)) + ctx.signers = signing_keys # do this twice so sigs can be verified with newly imported keys - parse_pgp_mime(email_bytes, gpgme_ctx) - email_struct = parse_pgp_mime(email_bytes, gpgme_ctx) + parse_pgp_mime(email_bytes, ctx) + email_struct = parse_pgp_mime(email_bytes, ctx) email_to, email_reply_to, email_subject = email_to_reply_to_subject(email_bytes) lang, reply_from = import_lang_pick_address(email_to, edward_config.hostname) @@ -285,12 +286,12 @@ def main (): replyinfo_obj.replies = lang.replies prepare_for_reply(email_struct, replyinfo_obj) - get_key_from_fp(replyinfo_obj, gpgme_ctx) + get_key_from_fp(replyinfo_obj, ctx) reply_plaintext = write_reply(replyinfo_obj) reply_mime = generate_encrypted_mime(reply_plaintext, email_reply_to, reply_from, \ email_subject, replyinfo_obj.encrypt_to_key, - gpgme_ctx) + ctx) if print_reply_only == True: print(reply_mime) @@ -318,22 +319,22 @@ def get_gpg_context (gnupghome, sign_with_key_fp): os.environ['GNUPGHOME'] = gnupghome - gpgme_ctx = gpgme.Context() - gpgme_ctx.armor = True + ctx = gpg.Context() + ctx.armor = True try: - sign_with_key = gpgme_ctx.get_key(sign_with_key_fp) - except gpgme.GpgmeError: + sign_with_key = ctx.get_key(sign_with_key_fp) + except gpg.errors.GPGMEError: error("unable to load signing key. is the gnupghome " + "and signing key properly set in the edward_config.py?") exit(1) - gpgme_ctx.signers = [sign_with_key] + ctx.signers = [sign_with_key] - return gpgme_ctx + return ctx -def parse_pgp_mime (email_bytes, gpgme_ctx): +def parse_pgp_mime (email_bytes, ctx): """Parses the email for mime payloads and decrypts/verfies signatures. This function creates a representation of a mime or plaintext email with @@ -343,7 +344,7 @@ def parse_pgp_mime (email_bytes, gpgme_ctx): Args: email_bytes: an email message in byte string format - gpgme_ctx: a gpgme context + ctx: a gpgme context Returns: A message as an instance of EddyMsg @@ -357,7 +358,7 @@ def parse_pgp_mime (email_bytes, gpgme_ctx): eddymsg_obj = parse_mime(email_struct) split_payloads(eddymsg_obj) - gpg_on_payloads(eddymsg_obj, gpgme_ctx) + gpg_on_payloads(eddymsg_obj, ctx) return eddymsg_obj @@ -567,7 +568,7 @@ def split_payload_pieces (eddymsg_obj, match_pair): eddymsg_obj.payload_pieces = new_pieces_list -def gpg_on_payloads (eddymsg_obj, gpgme_ctx, prev_parts=[]): +def gpg_on_payloads (eddymsg_obj, ctx, prev_parts=[]): """Performs GPG operations on the GPG parts of the message This function decrypts text, verifies signatures, and imports public keys @@ -576,7 +577,7 @@ def gpg_on_payloads (eddymsg_obj, gpgme_ctx, prev_parts=[]): Args: eddymsg_obj: an EddyMsg object with its payload_pieces split into GPG and non-GPG sections by split_payloads() - gpgme_ctx: a gpgme context + ctx: a gpgme context prev_parts: a list of mime parts that occur before the eddymsg_obj part, under the same multi-part mime part. This is used for @@ -599,7 +600,7 @@ def gpg_on_payloads (eddymsg_obj, gpgme_ctx, prev_parts=[]): if eddymsg_obj.multipart == True: prev_parts=[] for sub in eddymsg_obj.subparts: - gpg_on_payloads (sub, gpgme_ctx, prev_parts) + gpg_on_payloads (sub, ctx, prev_parts) prev_parts += [sub] return @@ -613,7 +614,7 @@ def gpg_on_payloads (eddymsg_obj, gpgme_ctx, prev_parts=[]): elif piece.piece_type == TxtType.message: piece.gpg_data = GPGData() - (plaintext_b, sigs, sigkey_missing, key_cannot_encrypt) = decrypt_block(piece.string, gpgme_ctx) + (plaintext_b, sigs, sigkey_missing, key_cannot_encrypt) = decrypt_block(piece.string, ctx) piece.gpg_data.sigkey_missing = sigkey_missing piece.gpg_data.key_cannot_encrypt = key_cannot_encrypt @@ -622,11 +623,11 @@ def gpg_on_payloads (eddymsg_obj, gpgme_ctx, prev_parts=[]): piece.gpg_data.decrypted = True piece.gpg_data.sigs = sigs # recurse! - piece.gpg_data.plainobj = parse_pgp_mime(plaintext_b, gpgme_ctx) + piece.gpg_data.plainobj = parse_pgp_mime(plaintext_b, ctx) continue # if not encrypted, check to see if this is an armored signature. - (plaintext_b, sigs, sigkey_missing, key_cannot_encrypt) = verify_sig_message(piece.string, gpgme_ctx) + (plaintext_b, sigs, sigkey_missing, key_cannot_encrypt) = verify_sig_message(piece.string, ctx) piece.gpg_data.sigkey_missing = sigkey_missing piece.gpg_data.key_cannot_encrypt = key_cannot_encrypt @@ -635,12 +636,12 @@ def gpg_on_payloads (eddymsg_obj, gpgme_ctx, prev_parts=[]): piece.piece_type = TxtType.signature piece.gpg_data.sigs = sigs # recurse! - piece.gpg_data.plainobj = parse_pgp_mime(plaintext_b, gpgme_ctx) + piece.gpg_data.plainobj = parse_pgp_mime(plaintext_b, ctx) elif piece.piece_type == TxtType.pubkey: piece.gpg_data = GPGData() - (key_fps, key_cannot_encrypt) = add_gpg_key(piece.string, gpgme_ctx) + (key_fps, key_cannot_encrypt) = add_gpg_key(piece.string, ctx) piece.gpg_data.key_cannot_encrypt = key_cannot_encrypt @@ -651,7 +652,7 @@ def gpg_on_payloads (eddymsg_obj, gpgme_ctx, prev_parts=[]): piece.gpg_data = GPGData() for prev in prev_parts: - (sig_fps, sigkey_missing, key_cannot_encrypt) = verify_detached_signature(piece.string, prev.payload_bytes, gpgme_ctx) + (sig_fps, sigkey_missing, key_cannot_encrypt) = verify_detached_signature(piece.string, prev.payload_bytes, ctx) piece.gpg_data.sigkey_missing = sigkey_missing piece.gpg_data.key_cannot_encrypt = key_cannot_encrypt @@ -902,7 +903,7 @@ def flatten_decrypted_payloads (eddymsg_obj, replyinfo_obj, get_signed_part): replyinfo_obj.msg_to_quote += piece.string -def get_key_from_fp (replyinfo_obj, gpgme_ctx): +def get_key_from_fp (replyinfo_obj, ctx): """Obtains a public key object from a key fingerprint If the .target_key is not set, then we use .fallback_target_key, if @@ -910,7 +911,7 @@ def get_key_from_fp (replyinfo_obj, gpgme_ctx): Args: replyinfo_obj: ReplyInfo instance - gpgme_ctx: the gpgme context + ctx: the gpgme context Return: Nothing @@ -931,9 +932,9 @@ def get_key_from_fp (replyinfo_obj, gpgme_ctx): for key in (replyinfo_obj.target_key, replyinfo_obj.fallback_target_key): if key != None: try: - encrypt_to_key = gpgme_ctx.get_key(key) + encrypt_to_key = ctx.get_key(key) - except gpgme.GpgmeError: + except gpg.errors.GPGMEError: continue if is_key_usable(encrypt_to_key): @@ -1034,7 +1035,7 @@ def write_reply (replyinfo_obj): return reply_plain -def add_gpg_key (key_block, gpgme_ctx): +def add_gpg_key (key_block, ctx): """Adds a GPG pubkey to the local keystore This adds keys received through email into the key store so they can be @@ -1042,7 +1043,7 @@ def add_gpg_key (key_block, gpgme_ctx): Args: key_block: the string form of the ascii-armored public key block - gpgme_ctx: the gpgme context + ctx: the gpgme context Returns: the fingerprint(s) of the imported key(s) which can be used for @@ -1053,9 +1054,9 @@ def add_gpg_key (key_block, gpgme_ctx): fp = io.BytesIO(key_block.encode('ascii')) try: - result = gpgme_ctx.import_(fp) + result = ctx.import_(fp) imports = result.imports - except gpgme.GpgmeError: + except gpg.errors.GPGMEError: imports = [] key_fingerprints = [] @@ -1065,7 +1066,7 @@ def add_gpg_key (key_block, gpgme_ctx): fingerprint = import_res[0] try: - key_obj = gpgme_ctx.get_key(fingerprint) + key_obj = ctx.get_key(fingerprint) except: key_obj = None @@ -1081,7 +1082,7 @@ def add_gpg_key (key_block, gpgme_ctx): return (key_fingerprints, key_cannot_encrypt) -def verify_sig_message (msg_block, gpgme_ctx): +def verify_sig_message (msg_block, ctx): """Verifies the signature of a signed, ascii-armored block of text. It encodes the string into ascii, since binary GPG files are currently @@ -1091,7 +1092,7 @@ def verify_sig_message (msg_block, gpgme_ctx): Args: msg_block: a GPG Message block in string form. It may be encrypted or not. If it is encrypted, it will return empty results. - gpgme_ctx: the gpgme context + ctx: the gpgme context Returns: A tuple containing the plaintext bytes of the signed part, the list of @@ -1105,19 +1106,21 @@ def verify_sig_message (msg_block, gpgme_ctx): block_b = io.BytesIO(msg_block.encode('ascii')) plain_b = io.BytesIO() + print(foo) try: - sigs = gpgme_ctx.verify(block_b, None, plain_b) - except gpgme.GpgmeError: + sigs = ctx.verify(msg_block) + except gpg.errors.GPGMEError as e: + print(e) return ("",[],False,False) plaintext_b = plain_b.getvalue() - (fingerprints, sigkey_missing, key_cannot_encrypt) = get_signature_fp(sigs, gpgme_ctx) + (fingerprints, sigkey_missing, key_cannot_encrypt) = get_signature_fp(sigs, ctx) return (plaintext_b, fingerprints, sigkey_missing, key_cannot_encrypt) -def verify_detached_signature (detached_sig, plaintext_bytes, gpgme_ctx): +def verify_detached_signature (detached_sig, plaintext_bytes, ctx): """Verifies the signature of a detached signature. This requires the signature part and the signed part as separate arguments. @@ -1125,7 +1128,7 @@ def verify_detached_signature (detached_sig, plaintext_bytes, gpgme_ctx): Args: detached_sig: the signature part of the detached signature plaintext_bytes: the byte form of the message being signed. - gpgme_ctx: the gpgme context + ctx: the gpgme context Returns: A tuple containging a list of encryption capable signing fingerprints @@ -1140,23 +1143,23 @@ def verify_detached_signature (detached_sig, plaintext_bytes, gpgme_ctx): plaintext_fp = io.BytesIO(plaintext_bytes) try: - sigs = gpgme_ctx.verify(detached_sig_fp, plaintext_fp, None) - except gpgme.GpgmeError: + sigs = ctx.verify(detached_sig_fp, plaintext_fp, None) + except gpg.errors.GPGMEError: return ([],False,False) - (fingerprints, sigkey_missing, key_cannot_encrypt) = get_signature_fp(sigs, gpgme_ctx) + (fingerprints, sigkey_missing, key_cannot_encrypt) = get_signature_fp(sigs, ctx) return (fingerprints, sigkey_missing, key_cannot_encrypt) -def decrypt_block (msg_block, gpgme_ctx): +def decrypt_block (msg_block, ctx): """Decrypts a block of GPG text and verifies any included sigatures. Some encypted messages have embeded signatures, so those are verified too. Args: msg_block: the encrypted(/signed) text - gpgme_ctx: the gpgme context + ctx: the gpgme context Returns: A tuple containing plaintext bytes, encryption-capable signatures (if @@ -1166,22 +1169,21 @@ def decrypt_block (msg_block, gpgme_ctx): signature keys are incapable of encryption. """ - block_b = io.BytesIO(msg_block.encode('ascii')) - plain_b = io.BytesIO() + block_b = msg_block.encode('ascii') try: - sigs = gpgme_ctx.decrypt_verify(block_b, plain_b) - except gpgme.GpgmeError: + plaintext_b, target_keys, verify_result = ctx.decrypt(block_b) + except gpg.errors.GPGMEError: return ("",[],False,False) - plaintext_b = plain_b.getvalue() - - (fingerprints, sigkey_missing, key_cannot_encrypt) = get_signature_fp(sigs, gpgme_ctx) + print(verify_result) + #(fingerprints, sigkey_missing, key_cannot_encrypt) = get_signature_fp(sigs, ctx) + fingerprints = verify_result.signatures[0].fpr return (plaintext_b, fingerprints, sigkey_missing, key_cannot_encrypt) -def get_signature_fp (sigs, gpgme_ctx): +def get_signature_fp (sigs, ctx): """Selects valid signatures from output of gpgme signature verifying functions get_signature_fp returns a list of valid signature fingerprints if those @@ -1189,7 +1191,7 @@ def get_signature_fp (sigs, gpgme_ctx): Args: sigs: a signature verification result object list - gpgme_ctx: a gpgme context + ctx: a gpgme context Returns: fingerprints: a list of fingerprints @@ -1204,9 +1206,9 @@ def get_signature_fp (sigs, gpgme_ctx): fingerprints = [] for sig in sigs: - if (sig.summary == 0) or (sig.summary & gpgme.SIGSUM_VALID != 0) or (sig.summary & gpgme.SIGSUM_GREEN != 0): + if (sig.summary == 0) or (sig.summary & gpg.SIGSUM_VALID != 0) or (sig.summary & gpg.SIGSUM_GREEN != 0): try: - key_obj = gpgme_ctx.get_key(sig.fpr) + key_obj = ctx.get_key(sig.fpr) except: if fingerprints == []: sigkey_missing = True @@ -1221,7 +1223,7 @@ def get_signature_fp (sigs, gpgme_ctx): key_cannot_encrypt = True elif fingerprints == []: - if (sig.summary & gpgme.SIGSUM_KEY_MISSING != 0): + if (sig.summary & gpg.SIGSUM_KEY_MISSING != 0): sigkey_missing = True return (fingerprints, sigkey_missing, key_cannot_encrypt) @@ -1335,7 +1337,7 @@ def import_lang_pick_address(email_to, hostname): def generate_encrypted_mime (plaintext, email_to, email_from, email_subject, - encrypt_to_key, gpgme_ctx): + encrypt_to_key, ctx): """This function creates the mime email reply. It can encrypt the email. If the encrypt_key is included, then the email is encrypted and signed. @@ -1347,7 +1349,7 @@ def generate_encrypted_mime (plaintext, email_to, email_from, email_subject, email_subject: the subject to use in reply encrypt_to_key: the key object to use for encrypting the email. (or None) - gpgme_ctx: the gpgme context + ctx: the gpgme context Returns A string version of the mime message, possibly encrypted and signed. @@ -1360,7 +1362,7 @@ def generate_encrypted_mime (plaintext, email_to, email_from, email_subject, encrypted_text = encrypt_sign_message(plaintext_mime.as_string(), encrypt_to_key, - gpgme_ctx) + ctx) control_mime = MIMEApplication("Version: 1", _subtype='pgp-encrypted', @@ -1435,7 +1437,7 @@ def email_quote_text (text): return quoted_message -def encrypt_sign_message (plaintext, encrypt_to_key, gpgme_ctx): +def encrypt_sign_message (plaintext, encrypt_to_key, ctx): """Encrypts and signs plaintext This encrypts and signs a message. @@ -1443,7 +1445,7 @@ def encrypt_sign_message (plaintext, encrypt_to_key, gpgme_ctx): Args: plaintext: text to sign and ecrypt encrypt_to_key: the key object to encrypt to - gpgme_ctx: the gpgme context + ctx: the gpgme context Returns: An encrypted and signed string of text @@ -1453,7 +1455,7 @@ def encrypt_sign_message (plaintext, encrypt_to_key, gpgme_ctx): plaintext_bytes = io.BytesIO(plaintext.encode('ascii')) encrypted_bytes = io.BytesIO() - gpgme_ctx.encrypt_sign([encrypt_to_key], gpgme.ENCRYPT_ALWAYS_TRUST, + ctx.encrypt_sign([encrypt_to_key], gpg.ENCRYPT_ALWAYS_TRUST, plaintext_bytes, encrypted_bytes) encrypted_txt = encrypted_bytes.getvalue().decode('ascii')