]> git.ipfire.org Git - pakfire.git/blob - python/pakfire/keyring.py
Warn if the host key is not in the key store.
[pakfire.git] / python / pakfire / keyring.py
1 #!/usr/bin/python
2 ###############################################################################
3 # #
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2012 Pakfire development team #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 import datetime
23 import gpgme
24 import io
25 import os
26
27 import logging
28 log = logging.getLogger("pakfire")
29
30 from constants import *
31 from i18n import _
32 from system import system
33
34 class Keyring(object):
35 def __init__(self, pakfire):
36 self.pakfire = pakfire
37
38 # Configure the environment.
39 os.environ["GNUPGHOME"] = self.path
40 self.create_path()
41
42 # Initialize context.
43 self.ctx = gpgme.Context()
44 self.ctx.armor = True
45
46 def __del__(self):
47 del os.environ["GNUPGHOME"]
48
49 @property
50 def path(self):
51 return KEYRING_DIR
52
53 def create_path(self):
54 if os.path.exists(self.path):
55 os.chmod(self.path, 700)
56 else:
57 os.makedirs(self.path, 700)
58
59 filename = os.path.join(self.path, "gnupg.conf")
60
61 if os.path.exists(filename):
62 os.chmod(filename, 600)
63 return
64
65 # Create a default gnupg.conf.
66 f = open(filename, "w")
67 f.write("# This is a default gnupg configuration file created by\n")
68 f.write("# Pakfire %s.\n" % PAKFIRE_VERSION)
69 f.close()
70
71 os.chmod(filename, 600)
72
73 def dump_key(self, keyfp):
74 key = self.get_key(keyfp, secret=False)
75 if not key:
76 return [" " + _("Not in key store: %s") % keyfp, ""]
77
78 ret = []
79 for uid in key.uids:
80 ret.append(uid.uid)
81
82 ret.append(" " + _("Fingerprint: %s") % keyfp)
83
84 key_priv = self.get_key(keyfp, secret=True)
85 if key_priv:
86 ret.append(" " + _("Private key available!"))
87 ret.append("")
88
89 for subkey in key.subkeys:
90 ret.append(" " + _("Subkey: %s") % subkey.keyid)
91 if subkey.expired:
92 ret.append(" %s" % _("This key has expired!"))
93
94 if subkey.secret:
95 ret.append(" %s" % _("This is a secret key."))
96
97 created = datetime.datetime.fromtimestamp(subkey.timestamp)
98 ret.append(" %s" % _("Created: %s") % created)
99 if subkey.expires:
100 expires = datetime.datetime.fromtimestamp(subkey.expires)
101 ret.append(" %s" % _("Expires: %s") % expires)
102 else:
103 ret.append(" %s" % _("This key does not expire."))
104
105 if subkey.pubkey_algo == gpgme.PK_RSA:
106 ret.append(" RSA/%s" % subkey.length)
107
108 ret.append("")
109
110 return ret
111
112 def get_keys(self):
113 """
114 Returns all keys that are known to the system.
115 """
116 return [k.subkeys[0].keyid for k in self.ctx.keylist(None, False)]
117
118 def get_key(self, keyid, secret=False):
119 try:
120 return self.ctx.get_key(keyid, secret)
121 except gpgme.GpgmeError:
122 return None
123
124 def get_host_key_id(self):
125 return self.pakfire.config.get("signatures", "host_key", None)
126
127 def get_host_key(self, secret=False):
128 key_id = self.get_host_key_id()
129
130 if key_id:
131 key = self.get_key(key_id, secret=secret)
132 return key_id
133
134 def gen_key(self, realname, email):
135 args = {
136 "realname" : realname,
137 "email" : email,
138 }
139
140 params = """
141 <GnupgKeyParms format="internal">
142 Key-Type: RSA
143 Key-Usage: sign
144 Key-Length: 4096
145 Name-Real: %(realname)s
146 Name-Email: %(email)s
147 Expire-Date: 0
148 </GnupgKeyParms>
149 """ % args
150
151 log.info(_("Generating new key for %(realname)s <%(email)s>...") % args)
152 log.info(_("This may take a while..."))
153
154 # Generate the key.
155 result = self.ctx.genkey(params)
156
157 # Dump the recently generated key.
158 for line in self.dump_key(result.fpr):
159 log.info(line)
160
161 # Return the fingerprint of the generated key.
162 return result.fpr
163
164 def import_key(self, keyfile):
165 ret = []
166
167 f = open(keyfile, "rb")
168 res = self.ctx.import_(f)
169 f.close()
170
171 log.info(_("Successfully imported %s.") % keyfile)
172
173 def export_key(self, keyid, keyfile):
174 keydata = io.BytesIO()
175 self.ctx.export(keyid, keydata)
176
177 f = open(keyfile, "wb")
178 f.write(keydata.getvalue())
179 f.close()
180
181 def delete_key(self, keyid):
182 key = self.ctx.get_key(keyid)
183 self.ctx.delete(key, True)
184
185 def list_keys(self):
186 ret = []
187
188 # Search for the host key and show it.
189 host_key = self.get_host_key(secret=True)
190 if host_key:
191 ret.append(_("Host key:"))
192 ret += [" %s" % l for l in self.dump_key(host_key)]
193 else:
194 host_key_id = self.get_host_key_id()
195 if host_key_id:
196 host_key = self.get_host_key(secret=False)
197 if host_key:
198 ret.append(_("WARNING! Host key with ID %s configured, but the secret key is missing!") \
199 % host_key_id)
200 else:
201 ret.append(_("WARNING! Host key with ID %s configured, but not found!") % host_key_id)
202 else:
203 ret.append(_("No host key available or configured."))
204
205 # List all other keys.
206 for key in self.get_keys():
207 # Skip the host key.
208 if key == host_key:
209 continue
210
211 ret += self.dump_key(key)
212
213 return ret
214
215 def sign(self, keyid, cleartext):
216 key = self.ctx.get_key(keyid, True)
217 assert key, "Key was not found or no secret key installed."
218
219 self.ctx.signers = [key,]
220
221 cleartext = io.BytesIO(cleartext)
222 signature = io.BytesIO()
223
224 self.ctx.sign(cleartext, signature, gpgme.SIG_MODE_DETACH)
225
226 return signature.getvalue()
227
228 def verify(self, signature, cleartext):
229 assert signature, "Empty signature?"
230
231 signature = io.BytesIO(signature)
232 cleartext = io.BytesIO(cleartext)
233
234 # Verify the data.
235 sigs = self.ctx.verify(signature, cleartext, None)
236
237 return sigs