Пример #1
0
 def prune(self):
     for msgid in self.keys():
         ctime = os.stat(self.path + util.tohex(msgid)).st_ctime
         diff = time.time() - ctime
         if diff > config.PRUNE_TIME:
             self.ignorekey(msgid)
             print("{} is too old, ignoring".format(util.tohex(msgid)))
Пример #2
0
 def ignorekey(self, key):
     with open("ignored-keys", "ab") as f:
         f.write(key)
     fname = util.tohex(key)
     if config.PRUNE_DELETE:
         os.remove(self.path + fname)
     else:
         os.rename(self.path + fname, self.path + "archive/" + fname)
Пример #3
0
 def write(self, code, data):
     cmd = struct.pack(
         "hBBiii",
         code,
         0x10,
         0,  # some flags?
         0,  # unknown
         len(data),
         0,  # unknown
     )
     print('write {:02x} {:08x} = {}'.format(code, len(data), tohex(data)))
     self.usb_write(cmd + data)
Пример #4
0
 def read(self, code, size):
     cmd = struct.pack(
         "hBBiii",
         code,
         0x11,
         0,  # some flags?
         0,  # unknown
         size,
         0,  # unknown
     )
     self.usb_write(cmd)
     res = self.usb_read(size)
     print('read {:02x} {:08x} = {}'.format(code, size, tohex(res)))
     return res
Пример #5
0
def main(argv):

    filename = ''
    text = ''
    usagetext = 'reader.py [-v]erbose -f <filename>\nreader.py [-v]erbose -t \'<CA FE BA BE...>\''
    verbosity = 0

    # setup known keys dictionarry by their device id
    keys = {
        '\x57\x00\x00\x44':
        '\xCA\xFE\xBA\xBE\x12\x34\x56\x78\x9A\xBC\xDE\xF0\xCA\xFE\xBA\xBE',
        '\x00\x00\x00\x00':
        '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'
    }

    try:
        opts, args = getopt.getopt(argv, "vt:f:", ["text=", "filename="])
    except getopt.GetoptError:
        print usagetext
        sys.exit(2)
    for opt, arg in opts:
        if opt in ("-f", "--filename"):
            filename = arg
            text = open(filename, 'r').read()
        if opt in ("-t", "--text"):
            text = arg
        if opt in ("-v"):
            verbosity = 2

    if verbosity > 0:
        print "filename: ", filename
        print "text: ", text
        print "verbosity: ", verbosity

    capture = bytearray().fromhex(text)

    if verbosity > 0:
        print "hex: ", util.tohex(capture)

    frame = WMBusFrame()
    frame.parse(capture, keys)
    frame.log(verbosity)
Пример #6
0
def main(argv):
    
    filename = ''
    text = ''
    usagetext = 'reader.py [-v]erbose -f <filename>\nreader.py [-v]erbose -t \'<CA FE BA BE...>\''
    verbosity = 0 

    # setup known keys dictionarry by their device id
    keys = {
    	'\x57\x00\x00\x44': '\xCA\xFE\xBA\xBE\x12\x34\x56\x78\x9A\xBC\xDE\xF0\xCA\xFE\xBA\xBE',
    	'\x00\x00\x00\x00': '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'
    }
    
    try:
        opts, args = getopt.getopt(argv,"vt:f:",["text=", "filename="])
    except getopt.GetoptError:
        print usagetext
        sys.exit(2)
    for opt, arg in opts:
        if opt in ("-f", "--filename"):
            filename = arg
            text = open(filename, 'r').read()
        if opt in ("-t", "--text"):
            text = arg
        if opt in ("-v"):
            verbosity = 2
    
    if verbosity > 0:
        print "verbosity: ", verbosity
        print "filename: ", filename
        print "txt: ",text

    capture = bytearray().fromhex(text)
    
    if verbosity > 0: 
        print "hex: ", util.tohex(capture)

    frame = WMBusFrame()
    frame.parse(capture, keys)
    frame.log(verbosity)
Пример #7
0
    def parse(self, arr, keys=None):
        """ Parses frame contents and initializes object values
        
        The first steps of setting up an WMBusFrame should be the 
        initialization of the class and passing the wM-Bus frame as an array
        to the parse method in order to initialize the object values. 
        
        Optionally, the parse method takes a keys dictionarry which lists
        known keys by their device id. E.g.
        
        keys = {
            '\x57\x00\x00\x44': '\xCA\xFE\xBA\xBE\x12\x34\x56\x78\x9A\xBC\xDE\xF0\xCA\xFE\xBA\xBE',
            '\x00\x00\x00\x00': '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'
        }
        """

        if len(arr)-1 != arr[0]:
            print "WARNING: frame length field does not match effective frame length! Decoding might be unreliable. Check your input."
            
            print "frame[0]: ", arr[0]
            print "len(frame)-1: ", len(arr)-1
        
        if (arr is not None and arr[0] >= 11):
            self.length = arr[0]
            self.control = arr[1]
            self.manufacturer = arr[2:4]
            self.address = arr[4:10]
            self.control_information = arr[10]
            self.data = arr[11:]
            
            if (self.is_with_long_tl()):
                self.header = WMBusLongDataHeader()
                self.header.parse(self.data[0:12])
                self.data = self.data[12:]
                
                '''
                Note that according to the standard, the manufacturer and 
                device id from the transport header have precedence over the
                frame information
                '''
                #self.manufacturer = header.manufacturer
                #self.address[0,4] = header.identification
                #self.address[4] = header.version
                #self.address[5] = header.device_type
                
            elif (self.is_with_short_tl()):
                self.header = WMBusShortDataHeader()
                self.header.parse(self.data[0:4])
                self.data = self.data[4:]
                
            self.data_size = len(self.data)
            
            if (keys):
                devid = ''.join(chr(b) for b in self.get_device_id()) 
                self.key = keys.get(devid, None)
            
            # time might come where we should move this into a function
            if (self.header and self.header.get_encryption_mode() == 5):
                
                # data is encrypted. thus, check if a key was specified
                if (self.key):
                    
                    # setup cipher specs, decrypt and strip padding
                    spec = AES.new(self.key, AES.MODE_CBC, "%s" % self.get_iv())
                    self.data = bytearray(spec.decrypt("%s" % self.data))
                   
                    if debug:
                        print "dec: ", util.tohex(self.data)
                    
                    # check whether the first two bytes are 2F
                    if (self.data[0:2] != '\x2F\x2F'):
                        print util.tohex(self.data)
                        raise Exception("Decryption failed")
            
            self.data = bytearray(self.data.lstrip('\x2F').rstrip('\x2F'))

            if debug:
                print "cut: ", util.tohex(self.data)

            while len(self.data) > 0:
                record = WMBusDataRecord()
                self.data = record.parse(self.data)            
                self.records.append(record)
        else:
            print "(%d) " % arr[0] + util.tohex(arr) 
            raise Exception("Invalid frame length")
Пример #8
0
 def log(self, verb):
     """ Print a log record for that frame
     
     The log record consist of the following information
     - timestamp
     - device manufacturer, serial, type and version
     - frame direction, purpose
     
     Depending on the verbosity, additional details could be printed
     - frame header info
     - transport header info
     - data records
     
     The log method takes three levels of verbosity
     0: just single line
     1: additionally log frame header and transpor header info
     2: additionally log data records
     """
     line = datetime.now().strftime("%b %d %H:%M:%S") + " "
     line += self.get_manufacturer_short() + " "
     line += util.tohex(self.get_device_id()) + " "
     line += self.get_function_code() + " "
     
     if self.records:
         line += 'Records: %d' % len(self.records)
         
         if verb >= 1:
             line += '\n--'
             line += "\nCI Detail:\t" + util.tohex(self.control_information) + " (" + self.get_ci_detail() + ", " + self.get_function_code() + ")"
             line += "\nheader:\t\t" + self.header_details()
             
             
             if (self.is_with_long_tl() or self.is_with_short_tl()):
                 line += "\nhas errors:\t%r" % self.header.has_errors()
                 line += "\naccess:\t\t" + self.header.accessibility()
                 
                 if (self.header.configuration):
                     line += "\nconfig word:\t" + util.tohex(self.header.configuration)
                     line += "\nmode:\t\t%d" % self.header.get_encryption_mode() + " (" + self.header.get_encryption_name() + ")"
                     
                     if (self.is_encrypted()):
                         line += "\niv:\t\t" + util.tohex(self.get_iv())
                         
                         if (self.key):
                             line += "\nkey:\t\t" + util.tohex(self.key)
                         else:
                             line += "\nkey:\t\tWARNING no suitable key configured"
             
             line += '\n--'
             
             if verb >= 2:
                 for rec in self.records:
                      val = rec.value
                      val.reverse()
                     
                      line += '\nDIFs:\t' + util.tohex(rec.header.dif) 
                      line += " (" + rec.header.get_function_field_name() 
                      line += ", " + rec.header.get_data_field_name() + ")"
                      
                      line += '\nVIFs:\t' + util.tohex(rec.header.vif) 
                      line += " (" + rec.header.get_vif_description() + ")"
                      
                      line += '\nValue:\t' + util.tohex(val)
                      line += '\n--'
                      
     else:
         line += 'Data: ' + util.tohex(self.data)
     '''
     line += "v%0.3d" % self.get_device_version() + " "
     line += self.get_device_type() + " (" + util.tohex(self.address[5]) + ") "
     '''
     print line
Пример #9
0
def main(argv):
    
    samplefile = ''
    interface = '/dev/ttyUSB3'
    usagetext = 'scanner.py -hv -i <interface>'
    verbosity = 0
    
    # setup known keys dictionarry by their device id
    keys = {
    	'\x57\x00\x00\x44': '\xCA\xFE\xBA\xBE\x12\x34\x56\x78\x9A\xBC\xDE\xF0\xCA\xFE\xBA\xBE',
    	'\x00\x00\x00\x00': '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'
    }
    
    try:
        opts, args = getopt.getopt(argv,"v:hi:",["interface="])
    except getopt.GetoptError:
        print usagetext
        sys.exit(2)
    for opt, arg in opts:
        if opt == '-h':
            print usagetext
            sys.exit()
        else:
            if opt in ("-i", "--interface"):
                interface = arg
            if opt == "-v":
                verbosity = 1
                
                if arg == 'v':
                    verbosity = 2
                
                if arg == 'vv':
                    verbosity = 3

    while 1:
        # setup values
        arr = bytearray()
        state = 0
        frame_length = -1
        
        # connect sniffer device
        ser = util.connect_sniffer(interface)

        # sleep for a while in case there is no data available
        while ser.inWaiting() == 0:
            time.sleep(2)

        # data arrived, go and get it
        while ser.inWaiting() > 0:

            if (state == 0):
                '''
                let's get the leading two bytes from the serial stream and
                check whether they match hex FF 03. Do this until we reach 
                the next FF 03 start sequence
                
                TODO:
                - How is the trailing byte checksum calculated?
                '''
                arr.append(ser.read(1))
                
                if (arr[0] == 0xFF):
					# found 0xFF, let's see whether the following byte is 0x03
                	arr.append(ser.read(1))
                	
                	if (arr[0] == 0xFF and len(arr) == 2 and arr[1] == 0x03):
						# just hit a valid start sequence => enter next state
						state = 1
                else:
                    '''
                    just hit an invalid start sequence. let's drop the bytes 
                    and start over
                    '''
                    arr = bytearray()
            elif (state == 1):
                # let's read the frame length from the next byte
                arr.append(ser.read(1))
                frame_length = arr[2] -1
                state = 2
            elif (state == 2):
                '''
                in case the payload length is greater than zero bytes, read 
                frame_length bytes from the serial stream
                '''
                if (len(arr)-3 < frame_length):
                    for i in range(frame_length):
                        arr.append(ser.read(1))
                        
                    if (verbosity >= 3):
                        # print the whole wireless M-Bus frame in hex
                        print util.tohex(arr)
                    
                    # instantiate an wireless m-bus frame based on the data
                    frame = WMBusFrame() 
                    frame.parse(arr[2:], keys)
                    
                    # print wM-Bus frame information as log line
                    frame.log(verbosity)
                
                # clear array and go to detect the next start sequence
                arr = bytearray()
                state = 0
Пример #10
0
 def __setitem__(self, key, value):
     if self.path == "---null---/":
         return
     itempath = self.path + util.tohex(key)
     with open(itempath, "wb") as f:
         f.write(value.serialise())
Пример #11
0
 def __getitem__(self, key):
     itempath = self.path + util.tohex(key)
     with open(itempath, "rb") as f:
         data = f.read()
     return message.from_serialised(data)
Пример #12
0
def runcmd(command, arguments):
    if command == "sync":
        try:
            if len(arguments) >= 2:
                client.sync(arguments[0], int(arguments[1]))
            elif len(arguments) == 1:
                client.sync(arguments[0])
            elif len(arguments) == 0:
                client.sync("localhost")

        except ConnectionRefusedError:
            print("Could not connect to daemon at address")
            return 1

        except (socket.gaierror, OSError):
            # The gaierror is thrown with an invalid hostname, and the OSError
            # is thrown with an IP such as 255.255.255.255.
            print("Malformed address")
            return 1

        except ValueError:
            print("Invalid port")
            return 1

        except OverflowError:
            print("Port must be between 0 and 65535")
            return 1

    elif command == "ls":
        for msgid in known_messages.keys():
            print(util.tohex(msgid))

    elif command == "help" and len(arguments) == 0:
        global first_help
        if first_help:
            print("Square brackets indicate optional arguments")
            print("Angle brackets indicate required arguments")
            print("A default is given for some commands")
            print("")
            first_help = False
        print("sync [IP = localhost] [PORT = 3514]")
        print("ls")
        print("help [COMMAND]")
        print("msg [recipients]")
        print("attach [recipients] <filename>")
        print("read <msgid>")
        print("quit")
    elif command == "help" and len(arguments) > 0:
        helpcmd = arguments[0]

        if helpcmd == "sync":
            print("Push and pull all your currently known messages to the")
            print("specified server. With no arguments, sync with own daemon")
            print("which won't do anything, but is good for debugging.")

        elif helpcmd == "ls":
            print("List all known messages by their msgid. Does not filter")
            print("for if you can actually read them at the moment")

        elif helpcmd == "help":
            print("You're using it now.")

        elif helpcmd == "attach":
            print("Allows you to insert binary files to the network.")
            print("Currently there are no restrictions, but obviously")
            print("inserting a 4GB file will be slow, and in the future")
            print("nodes may reject large files, or delete them sooner.")

        elif helpcmd == "read":
            print("Read the message specified by the msgid.")
            print("Partial ids are supported, with/without the 0x prefix.")

        elif helpcmd == "msg":
            print("Write textual messages in an editor")

        elif helpcmd == "quit":
            print("Quits the program, because ^C is too hard")
            print("This isn't the only quit string, however")

    elif command == "msg":
        recipients = arguments
        if len(recipients) == 0:
            print("Enter email addresses one by one for recipients")
            print("These must be the same as GPG knows them as")
            print("The above is very important, you might want to run")
            print("    gpg --list-keys")
            print("To make sure you're using the correct email")
            print("There is currently no error checking for invalid input")
            print()
            print("Enter a blank line to end input")
            while True:
                r = input("email: ")
                if r == "":
                    break
                recipients.append(r.strip())

        data = util.getinput()
        program = ["gpg", "--encrypt", "--sign"]
        for r in recipients:
            program.append("-r")
            program.append(r)
        encsign = subprocess.check_output(program, input=data.encode("UTF-8"))

        newmsg = message.message(encsign)
        known_messages[newmsg.msgid] = newmsg
        print("ID: {}".format(util.tohex(newmsg.msgid)))
        print("Message added to known messages")
        print("Run a sync against a known node, or wait for the syncd to run")

    elif command == "attach":
        if len(arguments) < 2:
            print("You need to include a recipient and the filename")
            return 1
        recipients = arguments[:-1]
        fname = arguments[-1]
        try:
            with open(fname, "rb") as f:
                data = f.read()
        except FileNotFoundError:
            print("Invalid filename")
            return 1

        program = ["gpg", "--encrypt", "--sign"]
        for r in recipients:
            program.append("-r")
            program.append(r)
        try:
            encsign = subprocess.check_output(program, input=data)
        except subprocess.CalledProcessError:
            print("Something happened, GPG was unable to encrypt it.")
            return 1
        newmsg = message.message(encsign)
        known_messages[newmsg.msgid] = newmsg
        print("ID: {}".format(util.tohex(newmsg.msgid)))
        print("Attachment added to known messages")
        print("Run a sync against a known node, or wait for the syncd to run")

    elif command == "read":
        if len(arguments) == 0:
            print("What do you want me to read?")
        else:
            foundmsgs = []
            wantedhex = arguments[0]
            if not(wantedhex.startswith("0x")):
                wantedhex = "0x" + wantedhex
            wantedhex = wantedhex.encode("ascii")

            for msgid in known_messages.keys():
                msgidhex = util.tohex(msgid)
                if msgidhex.startswith(wantedhex.decode("ascii")):
                    foundmsgs.append(msgid)

            if len(foundmsgs) > 1:
                print("More than one message found, be more specific")
            elif len(foundmsgs) == 0:
                print("No messages found")
            else:
                try:
                    msg = known_messages[foundmsgs[0]].gpg
                except ValueError:
                    print("Checksum failure on message")
                    print("The file is most likely corrupt, or there's a bug")
                    return 1
                try:
                    decrypted = subprocess.check_output("gpg", input=msg)
                except subprocess.CalledProcessError:
                    print("Something happened. GPG was unable to decrypt it")
                    return 1
                    # TODO This is because this wasn't actually addressed to
                    # the user. Maybe ls should filter out these?
                try:
                    decrypted = decrypted.decode("UTF-8")
                    if interactive:
                        util.writeoutput(decrypted)
                    else:
                        sys.stdout.write(decrypted)
                except UnicodeDecodeError:
                    # Likely a binary file.
                    if interactive:
                        print("This is a binary file.")
                        print("Enter a filename to save it as")
                        fname = input("fname: ")
                        with open(fname, "wb") as f:
                            f.write(decrypted)
                    else:
                        sys.stdout.buffer.write(decrypted)

    elif command in (":q", "quit", "exit", "bye"):
        sys.exit(0)

    else:
        print("Unknown command")