a signature key should be set as a fallback key
[edward.git] / edward
CommitLineData
0bec96d6 1#! /usr/bin/env python3
ff4136c7 2# -*- coding: utf-8 -*-
0bec96d6
AE
3"""*********************************************************************
4* Edward is free software: you can redistribute it and/or modify *
5* it under the terms of the GNU Affero Public License as published by *
6* the Free Software Foundation, either version 3 of the License, or *
7* (at your option) any later version. *
8* *
9* Edward is distributed in the hope that it will be useful, *
10* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12* GNU Affero Public License for more details. *
13* *
14* You should have received a copy of the GNU Affero Public License *
15* along with Edward. If not, see <http://www.gnu.org/licenses/>. *
16* *
17* Copyright (C) 2014-2015 Andrew Engelbrecht (AGPLv3+) *
18* Copyright (C) 2014 Josh Drake (AGPLv3+) *
19* Copyright (C) 2014 Lisa Marie Maginnis (AGPLv3+) *
8bdfb6d4
AE
20* Copyright (C) 2009-2015 Tails developers <tails@boum.org> ( GPLv3+) *
21* Copyright (C) 2009 W. Trevor King <wking@drexel.edu> ( GPLv2+) *
0bec96d6
AE
22* *
23* Special thanks to Josh Drake for writing the original edward bot! :) *
24* *
25************************************************************************
26
a5385c04 27Code sourced from these projects:
0bec96d6 28
8bdfb6d4
AE
29 * http://agpl.fsf.org/emailselfdefense.fsf.org/edward/CURRENT/edward.tar.gz
30 * https://git-tails.immerda.ch/whisperback/tree/whisperBack/encryption.py?h=feature/python3
31 * http://www.physics.drexel.edu/~wking/code/python/send_pgp_mime
0bec96d6
AE
32"""
33
34import sys
0bec96d6
AE
35import gpgme
36import re
37import io
40c37ab3 38import os
adcef2f7 39import importlib
0bec96d6 40
8bdfb6d4
AE
41import email.parser
42import email.message
43import email.encoders
44
45from email.mime.multipart import MIMEMultipart
46from email.mime.application import MIMEApplication
47from email.mime.nonmultipart import MIMENonMultipart
48
40c37ab3 49import edward_config
c96f3837 50
adcef2f7
AE
51langs = ["an", "de", "el", "en", "fr", "ja", "pt-br", "ro", "ru", "tr"]
52
38738401
AE
53match_types = [('clearsign',
54 '-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----'),
55 ('message',
56578eaf
AE
56 '-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----'),
57 ('pubkey',
58 '-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----'),
59 ('detachedsig',
38738401 60 '-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----')]
56578eaf
AE
61
62
63class EddyMsg (object):
64 def __init__(self):
65 self.multipart = False
66 self.subparts = []
67
68 self.charset = None
69 self.payload_bytes = None
70 self.payload_pieces = []
71
72 self.filename = None
73 self.content_type = None
74 self.description_list = None
75
76
77class PayloadPiece (object):
78 def __init__(self):
79 self.piece_type = None
80 self.string = None
81 self.gpg_data = None
82
83
38738401
AE
84class GPGData (object):
85 def __init__(self):
86 self.decrypted = False
87
88 self.plainobj = None
89 self.sigs = []
90 self.keys = []
91
d873ff48
AE
92class ReplyInfo (object):
93 def __init__(self):
94 self.replies = None
6906d46d
AE
95
96 self.target_key = None
97 self.fallback_target_key = None
d873ff48
AE
98 self.msg_to_quote = ""
99
100 self.success_decrypt = False
101 self.failed_decrypt = False
102 self.public_key_received = False
103 self.no_public_key = False
104 self.sig_success = False
105 self.sig_failure = False
106
38738401 107
0bec96d6
AE
108def main ():
109
20f6e7c5 110 handle_args()
0bec96d6 111
0a064403
AE
112 gpgme_ctx = get_gpg_context(edward_config.gnupghome,
113 edward_config.sign_with_key)
114
c96f3837 115 email_text = sys.stdin.read()
adcef2f7
AE
116 email_struct = parse_pgp_mime(email_text, gpgme_ctx)
117
118 email_to, email_from, email_subject = email_to_from_subject(email_text)
119 lang = import_lang(email_to)
fafa21c3 120
d873ff48
AE
121 replyinfo_obj = ReplyInfo()
122 replyinfo_obj.replies = lang.replies
123
124 prepare_for_reply(email_struct, replyinfo_obj)
013f7cb8 125 encrypt_to_key = get_key_from_fp(replyinfo_obj, gpgme_ctx)
d873ff48 126 reply_plaintext = write_reply(replyinfo_obj)
adcef2f7 127
2007103e
AE
128 # TODO error handling for missing keys and setting .no_public_key
129 reply_mime = generate_encrypted_mime(reply_plaintext, email_from, \
130 email_subject, encrypt_to_key,
131 gpgme_ctx)
1fccb295 132
2007103e 133 print(reply_mime)
c96f3837 134
0bec96d6 135
0a064403
AE
136def get_gpg_context (gnupghome, sign_with_key_fp):
137
138 os.environ['GNUPGHOME'] = gnupghome
139
140 gpgme_ctx = gpgme.Context()
141 gpgme_ctx.armor = True
142
143 try:
144 sign_with_key = gpgme_ctx.get_key(sign_with_key_fp)
145 except:
146 error("unable to load signing key. is the gnupghome "
147 + "and signing key properly set in the edward_config.py?")
148 exit(1)
149
150 gpgme_ctx.signers = [sign_with_key]
151
152 return gpgme_ctx
153
154
38738401 155def parse_pgp_mime (email_text, gpgme_ctx):
394a1476
AE
156
157 email_struct = email.parser.Parser().parsestr(email_text)
158
928e3819
AE
159 eddymsg_obj = parse_mime(email_struct)
160 split_payloads(eddymsg_obj)
161 gpg_on_payloads(eddymsg_obj, gpgme_ctx)
8bb4b0d5 162
928e3819 163 return eddymsg_obj
0bec96d6 164
0bec96d6 165
56578eaf 166def parse_mime(msg_struct):
0bec96d6 167
928e3819 168 eddymsg_obj = EddyMsg()
8bb4b0d5 169
56578eaf
AE
170 if msg_struct.is_multipart() == True:
171 payloads = msg_struct.get_payload()
0bec96d6 172
928e3819
AE
173 eddymsg_obj.multipart = True
174 eddymsg_obj.subparts = list(map(parse_mime, payloads))
dd11a483 175
56578eaf 176 else:
928e3819 177 eddymsg_obj = get_subpart_data(msg_struct)
394a1476 178
928e3819 179 return eddymsg_obj
c267c233 180
80119cab 181
56578eaf 182def scan_and_split (payload_piece, match_type, pattern):
cf75de65 183
a5d37d44
AE
184 # don't try to re-split pieces containing gpg data
185 if payload_piece.piece_type != "text":
186 return [payload_piece]
187
56578eaf
AE
188 flags = re.DOTALL | re.MULTILINE
189 matches = re.search("(?P<beginning>.*?)(?P<match>" + pattern +
190 ")(?P<rest>.*)", payload_piece.string, flags=flags)
86663388 191
56578eaf
AE
192 if matches == None:
193 pieces = [payload_piece]
c96f3837 194
56578eaf 195 else:
d437f8b2 196
56578eaf
AE
197 beginning = PayloadPiece()
198 beginning.string = matches.group('beginning')
199 beginning.piece_type = payload_piece.piece_type
d437f8b2 200
56578eaf
AE
201 match = PayloadPiece()
202 match.string = matches.group('match')
203 match.piece_type = match_type
d437f8b2 204
56578eaf
AE
205 rest = PayloadPiece()
206 rest.string = matches.group('rest')
207 rest.piece_type = payload_piece.piece_type
d437f8b2 208
56578eaf 209 more_pieces = scan_and_split(rest, match_type, pattern)
4615b156 210 pieces = [beginning, match ] + more_pieces
d437f8b2 211
56578eaf 212 return pieces
d437f8b2 213
d437f8b2 214
56578eaf 215def get_subpart_data (part):
0bec96d6 216
56578eaf 217 obj = EddyMsg()
0bec96d6 218
56578eaf
AE
219 obj.charset = part.get_content_charset()
220 obj.payload_bytes = part.get_payload(decode=True)
221
222 obj.filename = part.get_filename()
223 obj.content_type = part.get_content_type()
224 obj.description_list = part['content-description']
225
226 # your guess is as good as a-myy-ee-ine...
227 if obj.charset == None:
228 obj.charset = 'utf-8'
229
230 if obj.payload_bytes != None:
0eb75d9c
AE
231 try:
232 payload = PayloadPiece()
233 payload.string = obj.payload_bytes.decode(obj.charset)
234 payload.piece_type = 'text'
235
236 obj.payload_pieces = [payload]
237 except UnicodeDecodeError:
238 pass
56578eaf
AE
239
240 return obj
241
242
928e3819 243def do_to_eddys_pieces (function_to_do, eddymsg_obj, data):
56578eaf 244
928e3819
AE
245 if eddymsg_obj.multipart == True:
246 for sub in eddymsg_obj.subparts:
d873ff48 247 do_to_eddys_pieces(function_to_do, sub, data)
394a1476 248 else:
928e3819 249 function_to_do(eddymsg_obj, data)
dd11a483
AE
250
251
928e3819 252def split_payloads (eddymsg_obj):
a5d37d44
AE
253
254 for match_type in match_types:
928e3819 255 do_to_eddys_pieces(split_payload_pieces, eddymsg_obj, match_type)
a5d37d44 256
a5d37d44 257
928e3819 258def split_payload_pieces (eddymsg_obj, match_type):
a5d37d44
AE
259
260 (match_name, pattern) = match_type
261
262 new_pieces_list = []
928e3819 263 for piece in eddymsg_obj.payload_pieces:
a5d37d44
AE
264 new_pieces_list += scan_and_split(piece, match_name, pattern)
265
928e3819 266 eddymsg_obj.payload_pieces = new_pieces_list
a5d37d44
AE
267
268
928e3819 269def gpg_on_payloads (eddymsg_obj, gpgme_ctx, prev_parts=[]):
38738401 270
928e3819 271 if eddymsg_obj.multipart == True:
101d54a8 272 prev_parts=[]
928e3819 273 for sub in eddymsg_obj.subparts:
101d54a8
AE
274 gpg_on_payloads (sub, gpgme_ctx, prev_parts)
275 prev_parts += [sub]
38738401 276
d873ff48 277 return
38738401 278
928e3819 279 for piece in eddymsg_obj.payload_pieces:
38738401
AE
280
281 if piece.piece_type == "text":
282 # don't transform the plaintext.
283 pass
284
285 elif piece.piece_type == "message":
7e18f14d 286 (plaintext, sigs) = decrypt_block(piece.string, gpgme_ctx)
38738401
AE
287
288 if plaintext:
289 piece.gpg_data = GPGData()
b23e171d 290 piece.gpg_data.decrypted = True
38738401
AE
291 piece.gpg_data.sigs = sigs
292 # recurse!
293 piece.gpg_data.plainobj = parse_pgp_mime(plaintext, gpgme_ctx)
129543c3 294
8f61c66a 295 elif piece.piece_type == "pubkey":
f8ee6bd3 296 key_fps = add_gpg_key(piece.string, gpgme_ctx)
8f61c66a 297
f8ee6bd3 298 if key_fps != []:
8f61c66a 299 piece.gpg_data = GPGData()
f8ee6bd3 300 piece.gpg_data.keys = key_fps
129543c3
AE
301
302 elif piece.piece_type == "clearsign":
f8ee6bd3 303 (plaintext, sig_fps) = verify_clear_signature(piece.string, gpgme_ctx)
129543c3 304
f8ee6bd3 305 if sig_fps != []:
129543c3 306 piece.gpg_data = GPGData()
f8ee6bd3 307 piece.gpg_data.sigs = sig_fps
129543c3
AE
308 piece.gpg_data.plainobj = parse_pgp_mime(plaintext, gpgme_ctx)
309
101d54a8
AE
310 elif piece.piece_type == "detachedsig":
311 for prev in prev_parts:
312 payload_bytes = prev.payload_bytes
6d240f68 313 sig_fps = verify_detached_signature(piece.string, payload_bytes, gpgme_ctx)
101d54a8 314
6d240f68
AE
315 if sig_fps != []:
316 piece.gpg_data = GPGData()
317 piece.gpg_data.sigs = sig_fps
318 piece.gpg_data.plainobj = prev
319 break
7e18f14d 320
38738401
AE
321 else:
322 pass
323
324
928e3819 325def prepare_for_reply (eddymsg_obj, replyinfo_obj):
dd11a483 326
928e3819 327 do_to_eddys_pieces(prepare_for_reply_pieces, eddymsg_obj, replyinfo_obj)
56578eaf 328
928e3819 329def prepare_for_reply_pieces (eddymsg_obj, replyinfo_obj):
56578eaf 330
928e3819 331 for piece in eddymsg_obj.payload_pieces:
38738401 332 if piece.piece_type == "text":
d873ff48
AE
333 # don't quote the plaintext part.
334 pass
335
38738401 336 elif piece.piece_type == "message":
d873ff48
AE
337 if piece.gpg_data == None:
338 replyinfo_obj.failed_decrypt = True
339 else:
340 replyinfo_obj.success_decrypt = True
d873ff48 341
6906d46d
AE
342 if replyinfo_obj.target_key != None:
343 continue
344 if piece.gpg_data.sigs != []:
345 replyinfo_obj.target_key = piece.gpg_data.sigs[0]
346 replyinfo_obj.msg_to_quote = flatten_payloads(piece.gpg_data.plainobj)
347
348 # to catch public keys in encrypted blocks
d873ff48
AE
349 prepare_for_reply(piece.gpg_data.plainobj, replyinfo_obj)
350
8f61c66a 351 elif piece.piece_type == "pubkey":
d873ff48
AE
352 if piece.gpg_data == None:
353 replyinfo_obj.no_public_key = True
354 else:
355 replyinfo_obj.public_key_received = True
356
357 elif (piece.piece_type == "clearsign") \
358 or (piece.piece_type == "detachedsig"):
359 if piece.gpg_data == None:
360 replyinfo_obj.sig_failure = True
361 else:
362 replyinfo_obj.sig_success = True
363
ad07fe46 364 if replyinfo_obj.fallback_target_key == None:
6906d46d
AE
365 replyinfo_obj.fallback_target_key = piece.gpg_data.sigs[0]
366
367
d873ff48 368
928e3819 369def flatten_payloads (eddymsg_obj):
d873ff48
AE
370
371 flat_string = ""
372
0aa88c27
AE
373 if eddymsg_obj == None:
374 return ""
375
928e3819
AE
376 if eddymsg_obj.multipart == True:
377 for sub in eddymsg_obj.subparts:
d873ff48
AE
378 flat_string += flatten_payloads (sub)
379
380 return flat_string
381
6906d46d 382 # todo: don't include nested decrypted messages.
928e3819 383 for piece in eddymsg_obj.payload_pieces:
d873ff48
AE
384 if piece.piece_type == "text":
385 flat_string += piece.string
0aa88c27
AE
386 elif piece.piece_type == "message":
387 flat_string += flatten_payloads(piece.plainobj)
388 elif ((piece.piece_type == "clearsign") \
389 or (piece.piece_type == "detachedsig")) \
390 and (piece.gpg_data != None):
391 flat_string += flatten_payloads (piece.gpg_data.plainobj)
392
d873ff48
AE
393
394 return flat_string
395
396
013f7cb8
AE
397def get_key_from_fp (replyinfo_obj, gpgme_ctx):
398
dc24daf1
AE
399 if replyinfo_obj.target_key == None:
400 replyinfo_obj.target_key = replyinfo_obj.fallback_target_key
401
013f7cb8
AE
402 if replyinfo_obj.target_key != None:
403 try:
404 encrypt_to_key = gpgme_ctx.get_key(replyinfo_obj.target_key)
405 return encrypt_to_key
406
407 except:
408 pass
409
410 # no available key to use
411 replyinfo_obj.target_key = None
412 replyinfo_obj.fallback_target_key = None
413
414 replyinfo_obj.no_public_key = True
415 replyinfo_obj.public_key_received = False
416
417 return None
418
419
d873ff48
AE
420def write_reply (replyinfo_obj):
421
422 reply_plain = ""
423
424 if replyinfo_obj.success_decrypt == True:
d873ff48 425 reply_plain += replyinfo_obj.replies['success_decrypt']
2694709a
AE
426
427 if replyinfo_obj.no_public_key == False:
428 quoted_text = email_quote_text(replyinfo_obj.msg_to_quote)
429 reply_plain += quoted_text
d873ff48
AE
430
431 elif replyinfo_obj.failed_decrypt == True:
432 reply_plain += replyinfo_obj.replies['failed_decrypt']
433
434
435 if replyinfo_obj.sig_success == True:
436 reply_plain += "\n\n"
437 reply_plain += replyinfo_obj.replies['sig_success']
438
439 elif replyinfo_obj.sig_failure == True:
440 reply_plain += "\n\n"
441 reply_plain += replyinfo_obj.replies['sig_failure']
442
443
444 if replyinfo_obj.public_key_received == True:
445 reply_plain += "\n\n"
446 reply_plain += replyinfo_obj.replies['public_key_received']
447
448 elif replyinfo_obj.no_public_key == True:
449 reply_plain += "\n\n"
450 reply_plain += replyinfo_obj.replies['no_public_key']
451
452
453 reply_plain += "\n\n"
454 reply_plain += replyinfo_obj.replies['signature']
56578eaf 455
d873ff48 456 return reply_plain
56578eaf
AE
457
458
8f61c66a 459def add_gpg_key (key_block, gpgme_ctx):
c267c233 460
8f61c66a 461 fp = io.BytesIO(key_block.encode('ascii'))
c267c233 462
8f61c66a
AE
463 result = gpgme_ctx.import_(fp)
464 imports = result.imports
c267c233 465
f8ee6bd3 466 key_fingerprints = []
c267c233 467
8f61c66a
AE
468 if imports != []:
469 for import_ in imports:
470 fingerprint = import_[0]
f8ee6bd3 471 key_fingerprints += [fingerprint]
c267c233 472
e49673aa 473 debug("added gpg key: " + fingerprint)
ec1e779a 474
f8ee6bd3 475 return key_fingerprints
ec1e779a
AE
476
477
129543c3 478def verify_clear_signature (sig_block, gpgme_ctx):
cf75de65 479
129543c3
AE
480 # FIXME: this might require the un-decoded bytes
481 # or the correct re-encoding with the carset of the mime part.
482 msg_fp = io.BytesIO(sig_block.encode('utf-8'))
483 ptxt_fp = io.BytesIO()
cf75de65 484
129543c3 485 result = gpgme_ctx.verify(msg_fp, None, ptxt_fp)
cf75de65 486
129543c3
AE
487 # FIXME: this might require using the charset of the mime part.
488 plaintext = ptxt_fp.getvalue().decode('utf-8')
cf75de65 489
f8ee6bd3 490 sig_fingerprints = []
129543c3 491 for res_ in result:
f8ee6bd3 492 sig_fingerprints += [res_.fpr]
cf75de65 493
f8ee6bd3 494 return plaintext, sig_fingerprints
cf75de65
AE
495
496
101d54a8
AE
497def verify_detached_signature (detached_sig, plaintext_bytes, gpgme_ctx):
498
499 detached_sig_fp = io.BytesIO(detached_sig.encode('ascii'))
500 plaintext_fp = io.BytesIO(plaintext_bytes)
501 ptxt_fp = io.BytesIO()
502
503 result = gpgme_ctx.verify(detached_sig_fp, plaintext_fp, None)
504
505 sig_fingerprints = []
506 for res_ in result:
507 sig_fingerprints += [res_.fpr]
508
509 return sig_fingerprints
510
511
5b3053c1 512def decrypt_block (msg_block, gpgme_ctx):
0bec96d6 513
5b3053c1 514 block_b = io.BytesIO(msg_block.encode('ascii'))
0bec96d6
AE
515 plain_b = io.BytesIO()
516
afc1f64c
AE
517 try:
518 sigs = gpgme_ctx.decrypt_verify(block_b, plain_b)
519 except:
520 return ("",[])
0bec96d6 521
6aa41372 522 plaintext = plain_b.getvalue().decode('utf-8')
cbdf22c1
AE
523
524 fingerprints = []
525 for sig in sigs:
526 fingerprints += [sig.fpr]
527 return (plaintext, fingerprints)
0bec96d6
AE
528
529
d0489345 530def choose_reply_encryption_key (gpgme_ctx, fingerprints):
fafa21c3
AE
531
532 reply_key = None
d0489345
AE
533 for fp in fingerprints:
534 try:
535 key = gpgme_ctx.get_key(fp)
536
537 if (key.can_encrypt == True):
538 reply_key = key
539 break
540 except:
541 continue
542
fafa21c3 543
216708e9 544 return reply_key
fafa21c3
AE
545
546
d65993b8
AE
547def email_to_from_subject (email_text):
548
549 email_struct = email.parser.Parser().parsestr(email_text)
550
551 email_to = email_struct['To']
552 email_from = email_struct['From']
553 email_subject = email_struct['Subject']
554
555 return email_to, email_from, email_subject
556
557
adcef2f7
AE
558def import_lang(email_to):
559
5250b3b8
AE
560 if email_to != None:
561 for lang in langs:
562 if "edward-" + lang in email_to:
563 lang = "lang." + re.sub('-', '_', lang)
564 language = importlib.import_module(lang)
adcef2f7 565
5250b3b8 566 return language
adcef2f7
AE
567
568 return importlib.import_module("lang.en")
569
570
bf79a93e 571def generate_encrypted_mime (plaintext, email_from, email_subject, encrypt_to_key,
0a064403 572 gpgme_ctx):
1da9b527 573
2007103e
AE
574 # quoted printable encoding lets most ascii characters look normal
575 # before the decrypted mime message is decoded.
576 char_set = email.charset.Charset("utf-8")
577 char_set.body_encoding = email.charset.QP
8bdfb6d4 578
2007103e
AE
579 # MIMEText doesn't allow setting the text encoding
580 # so we use MIMENonMultipart.
581 plaintext_mime = MIMENonMultipart('text', 'plain')
582 plaintext_mime.set_payload(plaintext, charset=char_set)
216708e9
AE
583
584 if (encrypt_to_key != None):
8bdfb6d4
AE
585
586 encrypted_text = encrypt_sign_message(plaintext_mime.as_string(),
587 encrypt_to_key,
40c37ab3 588 gpgme_ctx)
8bdfb6d4
AE
589
590 control_mime = MIMEApplication("Version: 1",
591 _subtype='pgp-encrypted',
592 _encoder=email.encoders.encode_7or8bit)
593 control_mime['Content-Description'] = 'PGP/MIME version identification'
594 control_mime.set_charset('us-ascii')
595
596 encoded_mime = MIMEApplication(encrypted_text,
597 _subtype='octet-stream; name="encrypted.asc"',
598 _encoder=email.encoders.encode_7or8bit)
599 encoded_mime['Content-Description'] = 'OpenPGP encrypted message'
600 encoded_mime['Content-Disposition'] = 'inline; filename="encrypted.asc"'
601 encoded_mime.set_charset('us-ascii')
602
603 message_mime = MIMEMultipart(_subtype="encrypted", protocol="application/pgp-encrypted")
604 message_mime.attach(control_mime)
605 message_mime.attach(encoded_mime)
606 message_mime['Content-Disposition'] = 'inline'
216708e9 607
216708e9 608 else:
2007103e
AE
609 message_mime = plaintext_mime
610
611 message_mime['To'] = email_from
612 message_mime['Subject'] = email_subject
613
614 reply = message_mime.as_string()
1da9b527
AE
615
616 return reply
617
618
f87041f8
AE
619def email_quote_text (text):
620
621 quoted_message = re.sub(r'^', r'> ', text, flags=re.MULTILINE)
622
623 return quoted_message
624
625
0a064403 626def encrypt_sign_message (plaintext, encrypt_to_key, gpgme_ctx):
897cbaf6 627
6aa41372 628 plaintext_bytes = io.BytesIO(plaintext.encode('ascii'))
1da9b527
AE
629 encrypted_bytes = io.BytesIO()
630
897cbaf6 631 gpgme_ctx.encrypt_sign([encrypt_to_key], gpgme.ENCRYPT_ALWAYS_TRUST,
1da9b527
AE
632 plaintext_bytes, encrypted_bytes)
633
6aa41372 634 encrypted_txt = encrypted_bytes.getvalue().decode('ascii')
1da9b527
AE
635 return encrypted_txt
636
637
0a064403
AE
638def error (error_msg):
639
e4fb2ab2 640 sys.stderr.write(progname + ": " + str(error_msg) + "\n")
0a064403
AE
641
642
5e8f9094
AE
643def debug (debug_msg):
644
645 if edward_config.debug == True:
0a064403 646 error(debug_msg)
5e8f9094
AE
647
648
20f6e7c5
AE
649def handle_args ():
650 if __name__ == "__main__":
651
652 global progname
653 progname = sys.argv[0]
654
655 if len(sys.argv) > 1:
656 print(progname + ": error, this program doesn't " \
657 "need any arguments.", file=sys.stderr)
658 exit(1)
659
660
0bec96d6
AE
661main()
662