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