diff --git a/pace-gui/src/pace-gui.py b/pace-gui/src/pace-gui.py
index 1482acb..de93b02 100644
--- a/pace-gui/src/pace-gui.py
+++ b/pace-gui/src/pace-gui.py
@@ -7,16 +7,17 @@ try:
import pygtk
pygtk.require("2.0")
import gtk, gobject, glib, pango
-except:
+except ImportError:
sys.exit(1)
try:
import pace
-except:
+except ImportError:
print >> sys.stderr, "Could not import pace module, please install pyPACE"
sys.exit(1)
-from pace_gui_globals import *
+from pace_gui_globals import AT_CHAT_STRINGS, STR_CARD_FOUND, STR_NO_CARD, IMAGES #FIXME:Fuckup
+import pace_gui_globals as gui_globals
class MokoWindow(gtk.Window):
"""
@@ -36,7 +37,7 @@ class MokoWindow(gtk.Window):
assert(isinstance(self.title_str, basestring))
self.connect("destroy", gtk.main_quit)
- #Display resolution of OpenMoko minus height of the SHR toolbar
+ #Display resolution of OpenMoko minus height of the SHR toolbar
self.set_size_request(480, 640)
#self.set_resizable(False)
@@ -101,6 +102,9 @@ class MokoWindow(gtk.Window):
self.hide()
class CertificateDescriptionWindow(MokoWindow):
+ """This window is used to display information about the service provider,
+ extracted from the terminal certificate and the appendant certificate
+ description"""
def __init__(self, binDescription, binCert):
super(CertificateDescriptionWindow, self).__init__("Dienstanbieter", None)
@@ -208,8 +212,8 @@ class CVCWindow(MokoWindow):
chat = pace.cvc_get_chat(cvc)
self.chat = pace.get_binary_chat(chat)
self.rel_auth = []
- for c in self.chat:
- self.rel_auth.append(ord(c))
+ for char in self.chat:
+ self.rel_auth.append(ord(char))
self.rel_auth_len = len(self.rel_auth)
#Extract the access rights from the CHAT and display them in the window
@@ -219,7 +223,7 @@ class CVCWindow(MokoWindow):
if (i % 8 == 0):
j += 1
if self.rel_auth[self.rel_auth_len - j] & (1 << (i % 8)):
- chk = customCheckButton(AT_CHAT_STRINGS[i], i, self.body)
+ chk = customCheckButton(i, self.body)
self.access_rights.append(chk)
def btnForward_clicked(self, widget, data=None):
@@ -238,11 +242,13 @@ class CVCWindow(MokoWindow):
self.hide()
def __formatHexString(self, int_list):
+ """Take a list of integers and convert it to a hex string, where the
+ individual bytes are seperated by a colon (e.g. "0f:00:00")"""
hex_str = ""
for i in int_list:
- c = hex(i)[2:]
- if len(c) == 1: c = "0" + c
- hex_str += c + ":"
+ byte = hex(i)[2:]
+ if len(byte) == 1: byte = "0" + byte
+ hex_str += byte + ":"
return hex_str[:-1]
@@ -251,26 +257,41 @@ class customCheckButton(object):
The main difference isthat the checkbox can be placed at the right
side of the label and that we can store an index with the button"""
- def __init__(self, label, index, vbox):
+ def __init__(self, index, vbox):
+ self.idx = index
+ #Fetch name and (possibly empty) helptext
+ strings = AT_CHAT_STRINGS[index]
+ self.label = strings[0]
+ self.helptext = strings[1]
+
#Setup a label with the name of the access right
- self.lbl = gtk.Label(label)
+ self.lbl = gtk.Label(self.label)
self.lbl.set_alignment(0.0, 0.5)
self.lbl.set_padding(20, 0)
self.lbl.modify_font(pango.FontDescription("bold"))
- #an image at the left side of the label
- self.img = gtk.Image()
- self.img.set_from_file(image_dir + "/info.png")
+ #If we have a helptext, put an image at the left side of the label
+ self.info_box = None
+ if self.helptext is not None:
+ self.info_box = gtk.EventBox()
+ img = gtk.Image()
+ img.set_from_file(IMAGES["info"])
+ self.info_box.add(img)
+ self.info_box.connect("button_release_event", self._show_info)
#...and a checkbox on the right side of the label
- self.idx = index
self.chk = gtk.CheckButton("")
self.chk.set_active(True)
self.chk.set_alignment(1.0, 0.5)
#Insert the label and the checkbox in the vbox and add a seperator
hbox = gtk.HBox()
- hbox.pack_start(self.img, False, False)
+ if self.info_box is not None:
+ hbox.pack_start(self.info_box, False, False)
+ else: #pack empty label for spacing
+ spacer = gtk.Label("")
+ spacer.set_size_request(32, 32)
+ hbox.pack_start(spacer, False, False)
hbox.pack_start(self.lbl, True, True)
hbox.pack_start(self.chk, False, False)
vbox.pack_start(gtk.HSeparator())
@@ -279,6 +300,11 @@ class customCheckButton(object):
def is_active(self):
return self.chk.get_active()
+ def _show_info(self, widget=None, data=None):
+ """Show a messagebox containing info about the access right in
+ question"""
+ print self.helptext
+
class PinpadGTK:
"""This a simple GTK based GUI to enter a PIN/PUK/CAN"""
@@ -297,23 +323,23 @@ class PinpadGTK:
self.secret_len = 10
else:
raise ValueError("Unknwon secret type: %s" % self.secret)
- gtk.main_quit()
+ #gtk.main_quit()
self.chat = chat
self.cert = cert
#Set the Glade file
- self.gladefile = glade_dir + "/pinpad.glade"
+ self.gladefile = gui_globals.GLADE_FILE
self.builder = gtk.Builder()
try:
self.builder.add_from_file(self.gladefile)
- except glib.GError, e:
+ except glib.GError, err:
#If we encounter an exception at startup, we display a popup with
#the error message
popup = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
- gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, e.message)
+ gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, err.message)
popup.run()
popup.destroy()
- raise e
+ raise err
#Get the Main Window, and connect the "destroy" event
#Create our dictionay and connect it
@@ -344,10 +370,11 @@ class PinpadGTK:
lbl_cardStatus = self.builder.get_object("lbl_cardStatus")
#Fetch the status image
img_cardStatus = self.builder.get_object("img_cardStatus")
- img_cardStatus.set_from_file(image_dir + "/error.png")
+ img_cardStatus.set_from_file(IMAGES["error"])
- self.cardChecker = cardChecker(lbl_cardStatus, img_cardStatus, ePA_ATR)
- gobject.idle_add(self.cardChecker.start)
+ #We only start the thread for polling the card when the window
+ #is shown
+ self.cardChecker = None
#Change the font for the buttons
#For this you have to retrieve the label of each button and change
@@ -372,6 +399,11 @@ class PinpadGTK:
self.window.hide()
def show(self):
+ """Start the polling thread, then show the window"""
+ if self.cardChecker is None:
+ self.cardChecker = cardChecker(lbl_cardStatus, img_cardStatus,
+ gui_globals.ePA_ATR)
+ gobject.idle_add(self.cardChecker.start)
self.window.show_all()
def set_chat(self, chat):
@@ -379,8 +411,9 @@ class PinpadGTK:
def shutdown(self, widget):
"""Stop the cardChecker thread before exiting the application"""
- self.cardChecker.stop()
- self.cardChecker.join()
+ if self.cardChecker is not None:
+ self.cardChecker.stop()
+ self.cardChecker.join()
gtk.main_quit()
def digit_clicked(self, widget):
@@ -442,9 +475,9 @@ class PinpadGTK:
try:
#Stop polling the card while PACE is running
self.cardChecker.pause()
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env_args,
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env_args,
close_fds=True)
- except OSError, e:
+ except OSError:
popup = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL |
gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK, "pace-tool wurde nicht gefunden.")
@@ -455,7 +488,7 @@ class PinpadGTK:
#Show the animation to indicate that the program is not dead
waiting = gtk.Window(gtk.WINDOW_POPUP)
- animation = gtk.gdk.PixbufAnimation(image_dir + "/wait.gif")
+ animation = gtk.gdk.PixbufAnimation(IMAGES["wait"])
img = gtk.Image()
img.set_from_animation(animation)
waiting.add(img)
@@ -466,14 +499,14 @@ class PinpadGTK:
waiting.show_all()
#Try to keep the GUI responsive by taking care of the event queue
- line = p.stdout.readline()
+ line = proc.stdout.readline()
while line:
while gtk.events_pending():
gtk.main_iteration()
- line = p.stdout.readline()
+ line = proc.stdout.readline()
#Get the return value of the pace-tool process
- ret = p.poll()
+ ret = proc.poll()
waiting.destroy()
self.cardChecker.resume()
@@ -514,10 +547,10 @@ class cardChecker(Thread):
self._paused = Event()
self.lbl = lbl
self.img = img
- self.targetATR = atr
+ self.target_atr = atr
self.intervall = intervall
- def __cardCheck(self):
+ def _check_card(self):
"""
Actually this method should make use of pyscard, but I couldn't make
it run on the OpenMoko yet. Therefor we call opensc-tool instead, which
@@ -527,12 +560,12 @@ class cardChecker(Thread):
stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
line = proc.stdout.readline().rstrip()
- if line == self.targetATR:
+ if line == self.target_atr:
gobject.idle_add(self.lbl.set_label, STR_CARD_FOUND)
- gobject.idle_add(self.img.set_from_file, FILE_CARD_FOUND)
+ gobject.idle_add(self.img.set_from_file, IMAGES["apply"])
else:
gobject.idle_add(self.lbl.set_label, STR_NO_CARD)
- gobject.idle_add(self.img.set_from_file, FILE_NO_CARD)
+ gobject.idle_add(self.img.set_from_file, IMAGES["error"])
def run(self):
"""Main loop: Poll the card if the thread is not paused or finished"""
@@ -541,7 +574,7 @@ class cardChecker(Thread):
return
if self._paused.isSet():
continue
- self.__cardCheck()
+ self._check_card()
self._finished.wait(self.intervall)
def stop(self):
@@ -555,5 +588,6 @@ class cardChecker(Thread):
if __name__ == "__main__":
gobject.threads_init()
- CertificateDescriptionWindow(TEST_DESCRIPTION, TEST_CVC)
+ CertificateDescriptionWindow(gui_globals.TEST_DESCRIPTION,
+ gui_globals.TEST_CVC)
gtk.main()
diff --git a/pace-gui/src/pace_gui_globals.py.in b/pace-gui/src/pace_gui_globals.py.in
index 3dd6dff..55258ec 100644
--- a/pace-gui/src/pace_gui_globals.py.in
+++ b/pace-gui/src/pace_gui_globals.py.in
@@ -5,6 +5,8 @@ version = "@VERSION@"
image_dir = "@prefix@/share/" + name
glade_dir = "@prefix@/share/" + name
+GLADE_FILE = glade_dir + "/pinpad.glade"
+
STR_NO_CARD = "Ausweis"
STR_CARD_FOUND = "Ausweis"
@@ -13,46 +15,76 @@ FILE_CARD_FOUND = image_dir + "/apply.png"
ePA_ATR = "3b:84:80:01:00:00:90:00:95"
-AT_CHAT_STRINGS = [
- "Altersverifikation", #Age Verification
- u"Gemeindeschlüssel Verifikation", #Community ID Verification
- "Pseudonymfunktion", #Restrictied Identification
- "Priviligiertes Terminal", #Privileged Terminal
- "Verwendung der CAN", #CAN allowed
- "PIN Managment",
- "Zertifikat einspielen", #Install Certificate
- "Qualifiziertes Zertifikat einspielen", #Install Qualified Certificate
- "Dokumenttyp lesen", #Read DG 1
- "Staat lesen", #Read DG 2
- "Ablaufdatum lesen", #Read DG 3
- "Vorname lesen", #Read DG 4
- "Nachname lesen", #Read DG 5
- u"Künstlername lesen", #Read DG 6
- "Doktorgrad lesen", #Read DG 7
- "Geburtsdatum lesen", #Read DG 8
- "Geburtsort lesen", #Read DG 9
- u"Ungültig", #Read DG 10
- u"Ungültig", #Read DG 11
- u"Ungültig", #Read DG 12
- u"Ungültig", #Read DG 13
- u"Ungültig", #Read DG 14
- u"Ungültig", #Read DG 15
- u"Ungültig", #Read DG 16
- "Adresse lesen", #Read DG 17
- u"Gemeindeschlüssel lesen", #Read DG 18
- u"Ungültig", #Read DG 19
- u"Ungültig", #Read DG 20
- u"Ungültig", #Read DG 21
- u"Ungültig", #RFU
- u"Ungültig", #RFU
- u"Ungültig", #RFU
- u"Ungültig", #RFU
- u"Ungültig", #Write DG 21
- u"Ungültig", #Write DG 20
- u"Ungültig", #Write DG 19
- u"Gemeindeschlüssel schreiben", #Write DG 18
- u"Adresse schreiben", #Write DG 17"
-]
+#The following strings are used as help texts for eID access rights
+DOC_AGE_VERIFICATION = "foo"
+DOC_COMMUNITY_ID_VERIFICATION = "foo"
+DOC_RESTRICTED_ID = "foo"
+DOC_PRIVILEGED_TERMINAL = "foo"
+DOC_CAN_ALLOWED = "foo"
+DOC_PIN_MANAGMENT = "foo"
+DOC_INSTALL_CERTIFICATE = "foo"
+DOC_INSTALL_QC = "foo"
+DOC_DOCUMENT_TYPE = "foo"
+DOC_STATE = "foo"
+DOC_EXPIRY_DATE = "foo"
+
+IMAGES = {
+ "apply": image_dir + "/apply.png",
+ "error": image_dir + "/error.png",
+ "info" : image_dir + "/info.png",
+ "wait" : image_dir + "/wait.gif"
+}
+
+#Authentication Terminal CHAT
+AT_CHAT_STRINGS = {
+ #Age Verification
+ 0: ("Altersverifikation", DOC_AGE_VERIFICATION),
+ #Community ID Verification
+ 1: (u"Gemeindeschlüssel Verifikation", DOC_COMMUNITY_ID_VERIFICATION),
+ #Restrictied Identification
+ 2: ("Pseudonymfunktion", DOC_RESTRICTED_ID),
+ #Privileged Terminal
+ 3: ("Priviligiertes Terminal", DOC_PRIVILEGED_TERMINAL),
+ #CAN allowed
+ 4: ("Verwendung der CAN", DOC_CAN_ALLOWED),
+ 5: ("PIN Managment", DOC_PIN_MANAGMENT),
+ #Install Certificate
+ 6: ("Zertifikat einspielen", DOC_INSTALL_CERTIFICATE),
+ #Install Qualified Certificate
+ 7: ("Qualifiziertes Zertifikat einspielen", DOC_INSTALL_QC),
+ #Read DG 1
+ 8: ("Dokumenttyp", DOC_DOCUMENT_TYPE),
+ #Read DG 2
+ 9: ("Ausstellender Staat", DOC_STATE),
+ 10: ("Ablaufdatum", DOC_EXPIRY_DATE), #Read DG 3
+ 11: ("Vorname", None), #Read DG 4
+ 12: ("Nachname", None), #Read DG 5
+ 13: (u"Künstlername", None), #Read DG 6
+ 14: ("Doktorgrad", None), #Read DG 7
+ 15: ("Geburtsdatum", None), #Read DG 8
+ 16: ("Geburtsort", None), #Read DG 9
+ 17: (u"Ungültig", None), #Read DG 10
+ 18: (u"Ungültig", None), #Read DG 11
+ 19: (u"Ungültig", None),#Read DG 12
+ 20: (u"Ungültig", None),#Read DG 13
+ 21: (u"Ungültig", None),#Read DG 14
+ 22: (u"Ungültig", None),#Read DG 15
+ 23: (u"Ungültig", None),#Read DG 16
+ 24: ("Adresse", None), #Read DG 17
+ 25: (u"Gemeindeschlüssel lesen", None),#Read DG 18
+ 26: (u"Ungültig", None),#Read DG 19
+ 27: (u"Ungültig", None),#Read DG 20
+ 28: (u"Ungültig", None),#Read DG 21
+ 29: (u"Ungültig", None),#RFU
+ 30: (u"Ungültig", None),#RFU
+ 31: (u"Ungültig", None),#RFU
+ 32: (u"Ungültig", None),#RFU
+ 33: (u"Ungültig", None),#Write DG 21
+ 34: (u"Ungültig", None),#Write DG 20
+ 35: (u"Ungültig", None),#Write DG 19
+ 36: (u"Gemeindeschlüssel schreiben", None),#Write DG 18
+ 37: (u"Adresse schreiben", None)#Write DG 17"
+}
TEST_CVC = "\x7F\x21\x82\x01\x41\x7F\x4E\x81\xFA\x5F\x29\x01\x00\x42\x0D\x5A\x5A\x44\x56\x43\x41\x41\x54\x41\x30\x30\x30\x33\x7F\x49\x4F\x06\x0A\x04\x00\x7F\x00\x07\x02\x02\x02\x02\x03\x86\x41\x04\x19\xD1\x75\x45\xD3\xFE\x0B\x34\x3E\x7E\xE2\xAE\x4E\x2B\xC9\x2D\x51\x35\x1C\xC1\x17\xA4\x7F\xA9\x51\x9A\xDB\x1E\x40\x5E\xE6\xB8\x12\x12\x80\xBC\xC2\xFF\xF0\x35\x7A\x19\x7D\xE7\x39\xA7\xFD\x2E\xF0\x22\x10\xEF\x34\x3C\xDB\xE7\x9E\xF9\x4B\x8E\x28\x59\x1B\xB9\x5F\x20\x0B\x5A\x5A\x44\x4B\x42\x32\x30\x30\x30\x30\x52\x7F\x4C\x12\x06\x09\x04\x00\x7F\x00\x07\x03\x01\x02\x02\x53\x05\x00\x03\x01\xDF\x04\x5F\x25\x06\x01\x00\x00\x02\x01\x07\x5F\x24\x06\x01\x00\x00\x03\x03\x01\x65\x5E\x73\x2D\x06\x09\x04\x00\x7F\x00\x07\x03\x01\x03\x01\x80\x20\x75\xE0\xC4\xAC\x36\xC2\x5A\x33\xAC\x0E\x9A\x75\xEB\x79\x2A\x72\xF3\x31\xA5\x1E\x28\x63\x4E\xCC\x2E\xD6\x2E\x54\xF3\xC6\x93\xDA\x73\x2D\x06\x09\x04\x00\x7F\x00\x07\x03\x01\x03\x02\x80\x20\x18\x12\x65\x74\x49\xFC\xF1\xD3\xDA\xD8\x3D\x13\x14\x29\x17\x5C\x61\x8B\x21\xBA\xF0\xAF\x44\xAC\xE3\x8C\xB2\xC1\x2C\xEB\x2A\x56\x5F\x37\x40\x54\x0F\x85\x09\x12\xAB\xD3\x51\xF8\xF5\x56\x9B\x53\x4A\x5C\x8F\x64\x54\x5B\x51\xA7\x34\x70\xBE\x5A\xD2\x89\xC1\x9A\x5E\x13\x52\x53\xD3\xBB\x15\x52\x26\x21\x7B\x41\xE7\xF0\x68\xB3\x52\x3F\x3A\x63\x92\x22\xAF\x2B\x62\x8C\x39\x7D\x4F\xD4\x02\x1E\xDE\x00\xDC"