fix you shouldn't close the socket too early
[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.closeUds()
86             self.start()
87    
88     def start(self):
89         udsError = False
90         self.isStart = True
91         try:
92             self["resulttext"].setText("Please wait (Step 1)")
93             self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
94             self.uds.connect(("/var/run/tpmd_socket"))
95             self.uds.settimeout(5.0)
96         except:
97             self["resulttext"].setText("Security service not running.")
98             udsError = True
99         if not udsError:
100             if (self.stepFirst(TPMD_CMD_GET_DATA,[TPMD_DT_PROTOCOL_VERSION,TPMD_DT_TPM_VERSION,TPMD_DT_SERIAL])):
101                 try:  
102                     url = ("https://www.dream-multimedia-tv.de/verify/challenge?serial=%s&version=%s" % (self.serial,self.tpmdVersion))
103                     getPage(url).addCallback(self._gotPageLoadRandom).addErrback(self.errorLoad)
104                 except:
105                     self["resulttext"].setText("Can't connect to server. Please check your network!")
106
107     def _gotPageLoad(self, data):
108         authcode = data.strip().replace('+', '')
109         self.finish = "%s-%s-%s" % (authcode[0:4], authcode[4:8], authcode[8:12])
110         self["resulttext"].setText(self.finish)
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         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         self.parseResult(self.udsSend(typ,daten,len(daten)),2)
156         self.parseResult(self.udsSend(TPMD_CMD_COMPUTE_SIGNATURE,self.random,8),3)
157
158     def parseResult(self,rbuf,art):
159         if (rbuf != -1):
160             buf = self.formatList(rbuf)
161             if (art == 1):
162                 self.serial ="%d" % ((buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11])
163                 self.tpmdVersion = "%d" % (buf[1])
164                 self.protocolVersion = "%d" % buf[0]
165                 #print "serial:%s, version:%s, prot_version:%s" % (self.serial,self.tpmdVersion,self.protocolVersion)
166                 return True
167             elif (art == 2):
168                 tpmdata = {}
169                 while len(buf):
170                     type = buf[0]
171                     l = buf[1]
172                     data = ''.join([chr(x) for x in buf[2:l+2]])
173                     buf = buf[l+2:]
174                     tpmdata[type] = data
175                 
176                 self.level2_cert = tpmdata.get(4)
177                 self.level3_cert = tpmdata.get(5) # can be None
178                 self.fab_ca_cert = tpmdata.get(6)
179                 self.datablock_signed = tpmdata.get(7)                
180             elif (art == 3):
181                 self.r = self.formatString(buf)
182         else:
183             return False
184
185     def udsSend(self, cmdTyp, data, length):
186         sbuf = [(cmdTyp >> 8) & 0xff,(cmdTyp >> 0) & 0xff,(length >> 8) & 0xff,(length >> 0) & 0xff]
187         sbuf.extend(data[:length])
188         sbuf = struct.pack(str((length + 4))+"B", *sbuf)
189         try:
190             self.uds.send(sbuf)
191         except socket.timeout:
192             self["resulttext"].setText("Invalid response from Security service pls restart your Dreambox" )
193         rbuf = self.uds.recv(4)
194         leng = [ord(rbuf[2]) << 8 | ord(rbuf[3])]
195         if (leng != 4):
196             try:
197                 res = self.uds.recv(leng[0])
198             except socket.timeout:
199                 self["resulttext"].setText("Invalid response from Security service pls restart your Dreambox")
200         else:
201             return -1
202         return res
203
204     def closeUds(self):
205        try:
206             self.uds.close()
207        except:
208             pass
209
210     def exit(self):
211         self.closeUds()
212         self.close() 
213
214 def main(session, **kwargs):
215         session.open(genuineDreambox)
216
217 def Plugins(path,**kwargs):
218         global plugin_path
219         plugin_path = path
220         return [
221                 PluginDescriptor(name="Genuine Dreambox", description="Genuine Dreambox",where = PluginDescriptor.WHERE_PLUGINMENU, icon="genuine.png", fnc=main),
222                 PluginDescriptor(name="Genuine Dreambox", where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main)
223                 ]