small design fix
[enigma2-plugins.git] / genuinedreambox / src / plugin.py
1 # -*- coding: utf-8 -*-
2 ###########################################################################
3 #
4 # http://newnigma2.to
5 #
6 # $Id:
7 #
8 # Copyright (C) 2009 by
9 # <nixkoenner@newnigma2.to>
10 #
11 #       License: GPL
12 #
13 #       This program is free software; you can redistribute it and/or modify
14 #       it under the terms of the GNU General Public License as published by
15 #       the Free Software Foundation; either version 2 of the License, or
16 #       (at your option) any later version.
17 #
18 #       This program is distributed in the hope that it will be useful,
19 #       but WITHOUT ANY WARRANTY; without even the implied warranty of
20 #       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 #       GNU General Public License for more details.
22 #
23 #       You should have received a copy of the GNU General Public License
24 #       along with this program; if not, write to the Free Software
25 #       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #
27 ###########################################################################
28 #
29 # thx to <kayshadow@newnigma2.to> for painting the icon 
30 #
31 from Plugins.Plugin import PluginDescriptor
32 from Screens.Screen import Screen
33
34 from Components.ActionMap import ActionMap
35 from Components.Button import Button
36 from Components.Label import Label 
37
38 import socket
39 import struct
40 import base64
41
42 from twisted.web.client import getPage
43
44 TPMD_DT_RESERVED = 0x00
45 TPMD_DT_PROTOCOL_VERSION = 0x01
46 TPMD_DT_TPM_VERSION    = 0x02
47 TPMD_DT_SERIAL = 0x03
48 TPMD_DT_LEVEL2_CERT = 0x04
49 TPMD_DT_LEVEL3_CERT    = 0x05
50 TPMD_DT_FAB_CA_CERT    = 0x06
51 TPMD_DT_DATABLOCK_SIGNED = 0x07
52 TPMD_CMD_RESERVED    = 0x0000
53 TPMD_CMD_GET_DATA    = 0x0001
54 TPMD_CMD_APDU    = 0x0002
55 TPMD_CMD_COMPUTE_SIGNATURE = 0x0003
56
57 class genuineDreambox(Screen):
58     skin = """
59         <screen position="60,80" size="620,420" title="%s" >
60         <widget name="infotext" position="10,20" zPosition="1" size="600,150" font="Regular;20" halign="center" valign="center" />
61         <widget name="resulttext" position="10,160" zPosition="1" size="600,110" font="Regular;20" halign="center" valign="center" />
62         <widget name="infotext2" position="10,280" zPosition="1" size="600,80" font="Regular;20" halign="center" valign="center" />
63         <widget name="kRed" position="185,365" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />       
64         <ePixmap name="red" position="185,365" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
65         <widget name="kGreen" position="330,365" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
66         <ePixmap name="green" position="330,365" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
67         </screen>"""% _("Genuine Dreambox")
68
69     def __init__(self, session):
70         Screen.__init__(self, session)
71         self["actions"] = ActionMap(["SetupActions", "ColorActions"],
72         {
73             "green": self.restart,
74             "cancel": self.exit,
75          }, -1)
76         self["kGreen"] = Button(_("Restart"))
77         self["kRed"] = Button(_("Cancel"))
78         self["infotext"] = Label("With this plugin you can verify the authenticity of your Dreambox.\nFor additional information, \nplease visit our website \nhttps://www.dream-multimedia-tv.de.")
79         self["resulttext"] = Label("... Please wait ...")
80         self["infotext2"] = Label("Please visit our website and follow the instructions.\nAlternatively you can call our customer service hotline.")
81         self.onLayoutFinish.append(self.start)
82
83     def restart(self):
84         if not self.isStart:
85             self.start()
86    
87     def start(self):
88         udsError = False
89         self.isStart = True
90         try:
91             self["resulttext"].setText("Please wait (Step 1)")
92             self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
93             self.uds.connect(("/var/run/tpmd_socket"))
94             self.uds.settimeout(5.0)
95         except:
96             self["resulttext"].setText("Security service not running.")
97             udsError = True
98         if not udsError:
99             if (self.stepFirst(TPMD_CMD_GET_DATA,[TPMD_DT_PROTOCOL_VERSION,TPMD_DT_TPM_VERSION,TPMD_DT_SERIAL])):
100                 try:  
101                     url = ("https://www.dream-multimedia-tv.de/verify/challenge?serial=%s&version=%s" % (self.serial,self.tpmdVersion))
102                     getPage(url).addCallback(self._gotPageLoadRandom).addErrback(self.errorLoad)
103                 except:
104                     self["resulttext"].setText("Can't connect to server. Please check your network!")
105
106     def _gotPageLoad(self, data):
107         authcode = data.strip().replace('+', '')
108         self.finish = "%s-%s-%s" % (authcode[0:4], authcode[4:8], authcode[8:12])
109         self["resulttext"].setText(self.finish)
110         self.closeUds()
111         self.isStart = False
112         
113     def _gotPageLoadRandom(self, data):
114         self["resulttext"].setText("Please wait (Step 2)")
115         self.back = data.strip()
116         self.random = (self.formatList(base64.b64decode(self.back)))
117         if (self.stepSecond(TPMD_CMD_GET_DATA,[TPMD_DT_PROTOCOL_VERSION,TPMD_DT_TPM_VERSION,TPMD_DT_SERIAL,TPMD_DT_LEVEL2_CERT,
118                 TPMD_DT_LEVEL3_CERT,TPMD_DT_FAB_CA_CERT,TPMD_DT_DATABLOCK_SIGNED] )):
119             url = self.buildUrl()
120             getPage(url).addCallback(self._gotPageLoad).addErrback(self.errorLoad) 
121
122     def errorLoad(self, error):
123         print str(error)
124         self["resulttext"].setText("Invalid response from server. Please report: %s" % str(error))
125
126     def buildUrl(self):
127         # NOTE: this is a modified base64 which uses -_ instead of +/ to avoid the need for escpaing + when using urlencode 
128         tmpra = ("random=%s" % self.back.replace('+', '-').replace('/', '_'))
129         tmpl2 = ("&l2=%s" % base64.b64encode(self.level2_cert).replace('+', '-').replace('/', '_'))
130         if self.level3_cert:
131             tmpl3 = ("&l3=%s" % base64.b64encode(self.level3_cert).replace('+', '-').replace('/', '_'))
132         else:
133             tmpl3 = ""
134         tmpfa = ("&fab=%s" % base64.b64encode(self.fab_ca_cert).replace('+', '-').replace('/', '_'))
135         tmpda = ("&data=%s" % base64.b64encode(self.datablock_signed).replace('+', '-').replace('/', '_'))
136         tmpr  = ("&r=%s" % base64.b64encode(self.r).replace('+', '-').replace('/', '_'))
137         return("https://www.dream-multimedia-tv.de/verify/challenge?%s%s%s%s%s%s&serial=%s" % (tmpra,tmpl2,tmpl3,tmpfa,tmpda,tmpr,self.serial))
138
139     def formatList(self,l):
140         liste = []
141         for x in l:
142             liste.append(ord(x))
143         return liste
144     
145     def formatString(self,s):
146         myString = ""
147         for x in s:
148             myString =  myString + chr(x)
149         return myString
150
151     def stepFirst(self,typ,daten):
152         return (self.parseResult (self.udsSend(typ,daten,len(daten)), 1))
153
154     def stepSecond(self,typ,daten):
155         if (self.parseResult(self.udsSend(typ,daten,len(daten)),2) == False):
156             return False
157         if (self.parseResult(self.udsSend(TPMD_CMD_COMPUTE_SIGNATURE,self.random,8),3) == False):
158             return False
159         return True     
160
161     def parseResult(self,rbuf,art):
162         if (rbuf != -1):
163             buf = self.formatList(rbuf)
164             if (art == 1):
165                 self.serial ="%d" % ((buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11])
166                 self.tpmdVersion = "%d" % (buf[1])
167                 self.protocolVersion = "%d" % buf[0]
168                 #print "serial:%s, version:%s, prot_version:%s" % (self.serial,self.tpmdVersion,self.protocolVersion)
169                 return True
170             elif (art == 2):
171                 tpmdata = {}
172                 while len(buf):
173                     type = buf[0]
174                     l = buf[1]
175                     data = ''.join([chr(x) for x in buf[2:l+2]])
176                     buf = buf[l+2:]
177                     tpmdata[type] = data
178                 
179                 self.level2_cert = tpmdata.get(4)
180                 self.level3_cert = tpmdata.get(5) # can be None
181                 self.fab_ca_cert = tpmdata.get(6)
182                 self.datablock_signed = tpmdata.get(7)
183                 return True            
184             elif (art == 3):
185                 self.r = self.formatString(buf)
186                 return True
187         else:
188             return False
189
190     def udsSend(self, cmdTyp, data, length):
191         udsError = False
192         sbuf = [(cmdTyp >> 8) & 0xff,(cmdTyp >> 0) & 0xff,(length >> 8) & 0xff,(length >> 0) & 0xff]
193         sbuf.extend(data[:length])
194         sbuf = struct.pack(str((length + 4))+"B", *sbuf)
195         try:
196             self.uds.send(sbuf)
197             udsError = False
198         except socket.timeout:
199             udsError = True
200             self["resulttext"].setText("Invalid response from Security service pls restart your Dreambox" )
201         try:
202             rbuf = self.uds.recv(4)
203             udsError = False
204         except socket.timeout:
205             udsError = True
206             self["resulttext"].setText("Invalid response from Security service pls restart your Dreambox" )
207         
208         if (udsError == False):
209             leng = [ord(rbuf[2]) << 8 | ord(rbuf[3])]
210             if (leng != 4):
211                 try:
212                     res = self.uds.recv(leng[0])
213                 except socket.timeout:
214                     self["resulttext"].setText("Invalid response from Security service pls restart your Dreambox")
215             else:
216                 return -1
217         else:
218             return -1
219         return res
220
221     def closeUds(self):
222        try:
223             self.uds.close()
224        except:
225             pass
226
227     def exit(self):
228         self.closeUds()
229         self.close() 
230
231 def main(session, **kwargs):
232         session.open(genuineDreambox)
233
234 def Plugins(path,**kwargs):
235         global plugin_path
236         plugin_path = path
237         return [
238                 PluginDescriptor(name="Genuine Dreambox", description="Genuine Dreambox",where = PluginDescriptor.WHERE_PLUGINMENU, icon="genuine.png", fnc=main),
239                 PluginDescriptor(name="Genuine Dreambox", where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main)
240                 ]