1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
# vim:ts=4:sw=4:expandtab
import re
import pyme.core
import pyme.constants
def _engine_file_name_by_protocol(engines, protocol):
for engine in engines:
if engine.protocol == protocol:
return engine.file_name
return None
def RFC3156_micalg_from_result(result):
"""
Converts a GPGME hash algorithm name to one conforming to RFC3156.
GPGME returns hash algorithm names such as "SHA256", but RFC3156 says that
programs need to use names such as "pgp-sha256" instead.
:param result: GPGME op_sign_result() return value
:rtype: str
"""
# hash_algo will be something like SHA256, but we need pgp-sha256.
hash_algo = pyme.core.hash_algo_name(result.signatures[0].hash_algo)
return 'pgp-' + hash_algo.lower()
def RFC3156_canonicalize(text):
"""
Canonicalizes plain text (MIME-encoded usually) according to RFC3156.
This function works as follows (in that order):
1. Convert all line endings to \\\\r\\\\n (DOS line endings).
2. Ensure the text ends with a newline (\\\\r\\\\n).
3. Encode all occurences of "From " at the beginning of a line
to "From=20" in order to prevent other mail programs to replace
this with "> From" (to avoid MBox conflicts) and thus invalidate
the signature.
:param text: text to canonicalize (already encoded as quoted-printable)
:rtype: str
"""
text = re.sub("\r?\n", "\r\n", text)
if not text.endswith("\r\n"):
text += "\r\n"
text = re.sub("^From ", "From=20", text, flags=re.MULTILINE)
return text
class CryptoContext(pyme.core.Context):
"""
This is a wrapper around pyme.core.Context which simplifies the pyme API.
"""
def __init__(self):
pyme.core.Context.__init__(self)
gpg_path = _engine_file_name_by_protocol(pyme.core.get_engine_info(),
pyme.constants.PROTOCOL_OpenPGP)
if not gpg_path:
# TODO: proper exception
raise "no GPG engine found"
self.set_engine_info(pyme.constants.PROTOCOL_OpenPGP, gpg_path)
self.set_armor(1)
def detached_signature_for(self, plaintext_str):
"""
Signs the given plaintext string and returns the detached signature.
A detached signature in GPG speak is a separate blob of data containing
a signature for the specified plaintext.
.. note:: You should use #set_passphrase_cb before calling this method
if gpg-agent is not running.
::
context = crypto.CryptoContext()
def gpg_passphrase_cb(hint, desc, prev_bad):
return raw_input("Passphrase for key " + hint + ":")
context.set_passphrase_cb(gpg_passphrase_cb)
result, signature = context.detached_signature_for('Hello World')
if result is None:
return
:param plaintext_str: text to sign
:rtype: tuple of pyme.pygpgme._gpgme_op_sign_result and str
"""
plaintext_data = pyme.core.Data(plaintext_str)
signature_data = pyme.core.Data()
self.op_sign(plaintext_data, signature_data,
pyme.pygpgme.GPGME_SIG_MODE_DETACH)
result = self.op_sign_result()
signature_data.seek(0, 0)
signature = signature_data.read()
return result, signature
|