-Lots of refactoring

-Only start the card polling thread when the pinpad is shown
-Added an info button to all entrys in the CertificateDescriptionWindow that might not be clear to the user


git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@226 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
oepen
2010-07-17 11:35:31 +00:00
parent f508f0debb
commit 04e3a84da3
2 changed files with 146 additions and 80 deletions

View File

@@ -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()

View File

@@ -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 = "<span fgcolor=\"red\" font=\"12\">Ausweis</span>"
STR_CARD_FOUND = "<span fgcolor=\"green\" font=\"12\">Ausweis</span>"
@@ -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"