fix close socket
[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.close,
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         except:
95             self["resulttext"].setText("Security service not running.")
96             udsError = True
97         if not udsError:
98             if (self.stepFirst(TPMD_CMD_GET_DATA,[TPMD_DT_PROTOCOL_VERSION,TPMD_DT_TPM_VERSION,TPMD_DT_SERIAL])):
99                 try:  
100                     url = ("https://www.dream-multimedia-tv.de/verify/challenge?serial=%s&version=%s" % (self.serial,self.tpmdVersion))
101                     getPage(url).addCallback(self._gotPageLoadRandom).addErrback(self.errorLoad)
102                 except:
103                     self["resulttext"].setText("Can't connect to server. Please check your network!")
104         try:
105             self.uds.close()
106         except:
107             pass
108
109     def _gotPageLoad(self, data):
110         authcode = data.strip().replace('+', '')
111         self.finish = "%s-%s-%s" % (authcode[0:4], authcode[4:8], authcode[8:12])
112         self["resulttext"].setText(self.finish)
113         self.isStart = False
114         
115     def _gotPageLoadRandom(self, data):
116         self["resulttext"].setText("Please wait (Step 2)")
117         self.back = data.strip()
118         self.random = (self.formatList(base64.b64decode(self.back)))
119         self.stepSecond(TPMD_CMD_GET_DATA,[TPMD_DT_PROTOCOL_VERSION,TPMD_DT_TPM_VERSION,TPMD_DT_SERIAL,TPMD_DT_LEVEL2_CERT,
120                 TPMD_DT_LEVEL3_CERT,TPMD_DT_FAB_CA_CERT,TPMD_DT_DATABLOCK_SIGNED] )
121         url = self.buildUrl()
122         getPage(url).addCallback(self._gotPageLoad).addErrback(self.errorLoad) 
123
124     def errorLoad(self, error):
125         self["resulttext"].setText("Invalid response from server. Please report: %s" % str(error))
126
127     def buildUrl(self):
128         # NOTE: this is a modified base64 which uses -_ instead of +/ to avoid the need for escpaing + when using urlencode 
129         tmpra = ("random=%s" % self.back.replace('+', '-').replace('/', '_'))
130         tmpl2 = ("&l2=%s" % base64.b64encode(self.level2_cert).replace('+', '-').replace('/', '_'))
131         if self.level3_cert:
132             tmpl3 = ("&l3=%s" % base64.b64encode(self.level3_cert).replace('+', '-').replace('/', '_'))
133         else:
134             tmpl3 = ""
135         tmpfa = ("&fab=%s" % base64.b64encode(self.fab_ca_cert).replace('+', '-').replace('/', '_'))
136         tmpda = ("&data=%s" % base64.b64encode(self.datablock_signed).replace('+', '-').replace('/', '_'))
137         tmpr  = ("&r=%s" % base64.b64encode(self.r).replace('+', '-').replace('/', '_'))
138         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))
139
140     def formatList(self,l):
141         liste = []
142         for x in l:
143             liste.append(ord(x))
144         return liste
145     
146     def formatString(self,s):
147         myString = ""
148         for x in s:
149             myString =  myString + chr(x)
150         return myString
151
152     def stepFirst(self,typ,daten):
153         return (self.parseResult (self.udsSend(typ,daten,len(daten)), 1))
154
155     def stepSecond(self,typ,daten):
156         self.parseResult(self.udsSend(typ,daten,len(daten)),2)
157         self.parseResult(self.udsSend(TPMD_CMD_COMPUTE_SIGNATURE,self.random,8),3)
158
159     def parseResult(self,rbuf,art):
160         if (rbuf != -1):
161             buf = self.formatList(rbuf)
162             if (art == 1):
163                 self.serial ="%d" % ((buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11])
164                 self.tpmdVersion = "%d" % (buf[1])
165                 self.protocolVersion = "%d" % buf[0]
166                 #print "serial:%s, version:%s, prot_version:%s" % (self.serial,self.tpmdVersion,self.protocolVersion)
167                 return True
168             elif (art == 2):
169                 tpmdata = {}
170                 while len(buf):
171                     type = buf[0]
172                     l = buf[1]
173                     data = ''.join([chr(x) for x in buf[2:l+2]])
174                     buf = buf[l+2:]
175                     tpmdata[type] = data
176                 
177                 self.level2_cert = tpmdata.get(4)
178                 self.level3_cert = tpmdata.get(5) # can be None
179                 self.fab_ca_cert = tpmdata.get(6)
180                 self.datablock_signed = tpmdata.get(7)                
181             elif (art == 3):
182                 self.r = self.formatString(buf)
183         else:
184             return False
185
186     def udsSend(self, cmdTyp, data, length):
187         self.uds.settimeout(2)
188         sbuf = [(cmdTyp >> 8) & 0xff,(cmdTyp >> 0) & 0xff,(length >> 8) & 0xff,(length >> 0) & 0xff]
189         sbuf.extend(data[:length])
190         sbuf = struct.pack(str((length + 4))+"B", *sbuf)
191         try:
192             self.uds.send(sbuf)
193         except socket.timeout:
194             self["resulttext"].setText("Invalid response from Security service pls restart your Dreambox" )
195         rbuf = self.uds.recv(4)
196         leng = [ord(rbuf[2]) << 8 | ord(rbuf[3])]
197         if (leng != 4):
198             try:
199                 res = self.uds.recv(leng[0])
200             except socket.timeout:
201                 self["resulttext"].setText("Invalid response from Security service pls restart your Dreambox")
202         else:
203             return -1
204         return res
205
206 def main(session, **kwargs):
207         session.open(genuineDreambox)
208
209 def Plugins(path,**kwargs):
210         global plugin_path
211         plugin_path = path
212         return [
213                 PluginDescriptor(name="Genuine Dreambox", description="Genuine Dreambox",where = PluginDescriptor.WHERE_PLUGINMENU, icon="genuine.png", fnc=main),
214                 PluginDescriptor(name="Genuine Dreambox", where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main)
215                 ]