Example #1
0
    def setupSingleNotification(self, serviceUUID, characteristicUUID, writeCommand):
        characteristic = self.getCharacteristic(serviceUUID, characteristicUUID)
        peripheral = self.connectedPeripherals[self.connectedPeripheral]
        
        peripheral.withDelegate(PeripheralDelegate(lambda x: self._killNotificationLoop(x), self.settings))
        
        characteristicCCCDList = characteristic.getDescriptors(forUUID=CCCD_UUID)
        if len(characteristicCCCDList) == 0:
            raise BluenetBleException(BleError.CAN_NOT_FIND_CCCD, "Can not find CCCD handle to use notifications for characteristic: " + characteristicUUID)
        
        characteristicCCCD = characteristicCCCDList[0]
        
        # enable notifications.. This is ugly but necessary
        characteristicCCCD.write(b"\x01\x00", True)
        
        # execute something that will trigger the notifications
        writeCommand()
        
        self.notificationLoopActive = True

        loopCount = 0
        while self.notificationLoopActive and loopCount < 10:
            peripheral.waitForNotifications(0.5)
            loopCount += 1


        if self.notificationResult is None:
            raise BluenetBleException(BleError.NO_NOTIFICATION_DATA_RECEIVED, "No notification data received.")
        
        result = self.notificationResult
        self.notificationResult = None
        
        return result
Example #2
0
    def encrypt(dataArray, settings):
        if settings.sessionNonce is None:
            raise BluenetBleException(BleError.NO_SESSION_NONCE_SET,
                                      "Can't Decrypt: No session nonce set")

        if settings.userLevel == UserLevel.unknown:
            raise BluenetBleException(
                BleError.NO_ENCRYPTION_KEYS_SET,
                "Can't Decrypt: No encryption keys set.")

        packetNonce = [0] * PACKET_NONCE_LENGTH
        # create a random nonce
        for i in range(0, PACKET_NONCE_LENGTH):
            packetNonce[i] = EncryptionHandler.getRandomNumber()

        key = EncryptionHandler._getKey(settings)
        encryptedData = EncryptionHandler.encryptCTR(dataArray, packetNonce,
                                                     settings.sessionNonce,
                                                     key)

        result = packetNonce + [settings.userLevel.value]

        for byte in encryptedData:
            result.append(byte)

        return bytes(result)
Example #3
0
    def __init__(self, dataArray):
        prefixLength = PACKET_NONCE_LENGTH + PACKET_USER_LEVEL_LENGTH
        # 20 is the minimal size of a packet (3+1+16)
        if len(dataArray) < 20:
            raise BluenetBleException(BleError.INVALID_ENCRYPTION_PACKAGE, "Invalid package for encryption. It is too short (min length 20) got " + str(len(dataArray)) + " bytes.")

        self.nonce = [0] * PACKET_NONCE_LENGTH
        
        for i in range(0, PACKET_NONCE_LENGTH):
            self.nonce[i] = dataArray[i]
            
        if dataArray[PACKET_NONCE_LENGTH] > 2 and dataArray[PACKET_NONCE_LENGTH] != UserLevel.setup.value:
            raise BluenetBleException(BleError.INVALID_ENCRYPTION_USER_LEVEL, "User level in read packet is invalid:" + str(dataArray[PACKET_NONCE_LENGTH]))
        
        try:
            self.userLevel = UserLevel(dataArray[PACKET_NONCE_LENGTH])
        except ValueError:
            raise BluenetBleException(BleError.INVALID_ENCRYPTION_USER_LEVEL, "User level in read packet is invalid:" + str(dataArray[PACKET_NONCE_LENGTH]))
        
        payload = [0] * (len(dataArray) - prefixLength)
        for i in range(0, (len(dataArray) - prefixLength)):
            payload[i] = dataArray[i + prefixLength]
            
        if len(payload) % 16 != 0:
            raise BluenetBleException(BleError.INVALID_ENCRYPTION_PACKAGE, "Invalid size for encrypted payload")
        
        self.payload = payload
Example #4
0
 def decryptSessionNonce(inputData, key):
     if len(inputData) == 16:
         decrypted = EncryptionHandler.decryptECB(inputData, key)
         checksum = Conversion.uint8_array_to_uint32(decrypted)
         if checksum == CHECKSUM:
             return [decrypted[4], decrypted[5], decrypted[6], decrypted[7], decrypted[8]]
         else:
             raise BluenetBleException(BleError.COULD_NOT_VALIDATE_SESSION_NONCE, "Could not validate the session nonce.")
 
     else:
         raise BluenetBleException(BleError.COULD_NOT_READ_SESSION_NONCE, "Could not read session nonce, maybe encryption is disabled?")
 def loadSettingsFromFile(self, path):
     fileReader = JsonFileStore(path)
     data = fileReader.getData()
     
     if "admin" not in data:
         raise BluenetBleException(BluenetError.ADMIN_KEY_REQURED)
     if "member" not in data:
         raise BluenetBleException(BluenetError.MEMBER_KEY_REQUIRED)
     if "guest" not in data:
         raise BluenetBleException(BluenetError.GUEST_KEY_REQURED)
     
     self.setSettings(data["admin"], data["member"], data["guest"])
Example #6
0
    def setupNotificationStream(self, serviceUUID, characteristicUUID,
                                writeCommand, resultHandler, timeout):
        characteristic = self.getCharacteristic(serviceUUID,
                                                characteristicUUID)
        peripheral = self.connectedPeripherals[self.connectedPeripheral]

        peripheral.withDelegate(
            PeripheralDelegate(lambda x: self._loadNotificationResult(x),
                               self.settings))

        characteristicCCCDList = characteristic.getDescriptors(
            forUUID=CCCD_UUID)
        if len(characteristicCCCDList) == 0:
            raise BluenetBleException(
                BleError.CAN_NOT_FIND_CCCD,
                "Can not find CCCD handle to use notifications for characteristic: "
                + characteristicUUID)

        characteristicCCCD = characteristicCCCDList[0]

        # enable notifications.. This is ugly but necessary
        characteristicCCCD.write(b"\x01\x00", True)

        # execute something that will trigger the notifications
        writeCommand()

        self.notificationLoopActive = True
        self.notificationResult = None

        loopCount = 0
        successful = False
        while self.notificationLoopActive and loopCount < timeout * 2:
            peripheral.waitForNotifications(0.5)
            loopCount += 1
            if self.notificationResult is not None:
                command = resultHandler(self.notificationResult)
                self.notificationResult = None
                if command == ProcessType.ABORT_ERROR:
                    self.notificationLoopActive = False
                    raise BluenetBleException(
                        BleError.ABORT_NOTIFICATION_STREAM_W_ERROR,
                        "Aborting the notification stream because the resultHandler raised an error."
                    )
                elif command == ProcessType.FINISHED:
                    self.notificationLoopActive = False
                    successful = True

        if not successful:
            raise BluenetBleException(
                BleError.NOTIFICATION_STREAM_TIMEOUT,
                "Notification stream not finished within timeout.")
Example #7
0
    def getCharacteristics(self, serviceUUID):
        if self.connectedPeripheral:
            peripheral = self.connectedPeripherals[self.connectedPeripheral]

            try:
                service = peripheral.getServiceByUUID(serviceUUID)
            except BTLEException:
                raise BluenetBleException(BleError.CAN_NOT_FIND_SERVICE, "Can not find service: " + serviceUUID)

            characteristics = service.getCharacteristics()

            return characteristics

        else:
            raise BluenetBleException(BleError.CAN_NOT_GET_CHACTERISTIC, "Can't get characteristics: Not connected.")
Example #8
0
 def decrypt(data, settings):
     if settings.sessionNonce is None:
         raise BluenetBleException(BleError.NO_SESSION_NONCE_SET, "Can't Decrypt: No session nonce set")
 
     if settings.userLevel == UserLevel.unknown:
         raise BluenetBleException(BleError.NO_ENCRYPTION_KEYS_SET, "Can't Decrypt: No encryption keys set.")
     
     #unpack the session data
     sessionData = SessionData(settings.sessionNonce)
     package = EncryptedPackage(data)
     
     key = EncryptionHandler._getKeyForLevel(package.userLevel, settings)
 
     # decrypt data
     decrypted = EncryptionHandler.decryptCTR(package.payload, package.nonce, sessionData.sessionNonce, key)
     
     return EncryptionHandler._verifyDecryption(decrypted, sessionData.validationKey)
Example #9
0
 def _getKeyForLevel(userLevel, settings):
     if settings.initializedKeys == False and userLevel != UserLevel.setup:
         raise BluenetBleException(BleError.NO_ENCRYPTION_KEYS_SET, "Could not encrypt: Keys not set.")
 
     key = None
     if userLevel == UserLevel.admin:
         key = settings.adminKey
     elif userLevel == UserLevel.member:
         key = settings.memberKey
     elif userLevel == UserLevel.guest:
         key = settings.guestKey
     elif userLevel == UserLevel.setup:
         key = settings.setupKey
     else:
         raise BluenetBleException(BleError.NO_ENCRYPTION_KEYS_SET, "Could not encrypt: Invalid key for encryption.")
 
     if key is None:
         raise BluenetBleException(BleError.NO_ENCRYPTION_KEYS_SET, "Could not encrypt: Keys not set.")
 
     return key
Example #10
0
 def _verifyDecryption(decrypted, validationKey):
     # the conversion to uint32 only takes the first 4 bytes
     if Conversion.uint8_array_to_uint32(decrypted) == Conversion.uint8_array_to_uint32(validationKey):
         # remove checksum from decryption and return payload
         result = [0] * (len(decrypted) - SESSION_KEY_LENGTH)
         for i in range(0,len(result)):
             result[i] = decrypted[i+SESSION_KEY_LENGTH]
         return result
 
     else:
         raise BluenetBleException(BleError.ENCRYPTION_VALIDATION_FAILED, "Failed to validate result, Could not decrypt")
Example #11
0
 def __init__(self, sessionData):
     if len(sessionData) != SESSION_DATA_LENGTH:
         raise BluenetBleException(BleError.INVALID_SESSION_DATA, "Invalid Session Data")
     
     self.sessionNonce  = [0] * SESSION_DATA_LENGTH
     self.validationKey = [0] * SESSION_KEY_LENGTH
     
     for i in range(0,SESSION_KEY_LENGTH):
         self.sessionNonce[i]  = sessionData[i]
         self.validationKey[i] = sessionData[i]
     
     self.sessionNonce[SESSION_DATA_LENGTH-1] = sessionData[SESSION_DATA_LENGTH-1]
    def _writeConfigPacket(self, packet):
        self.core.ble.writeToCharacteristic(CSServices.SetupService,
                                            SetupCharacteristics.ConfigControl,
                                            packet)

        returnCode = self.core.ble.readCharacteristic(
            CSServices.SetupService, SetupCharacteristics.ConfigControl)

        if returnCode[0] != 0:
            raise BluenetBleException(
                BleError.RETURN_CODE_NOT_SUCCESSFUL,
                "The Crownstone returned a different code than 0 as a response to the config control write: "
                + str(returnCode[0]))
    def classicSetup(self, crownstoneId, meshAccessAddress, ibeaconUUID,
                     ibeaconMajor, ibeaconMinor):
        if not self.core.settings.initializedKeys:
            raise BluenetBleException(
                BleError.NO_ENCRYPTION_KEYS_SET,
                "Keys are not initialized so I can't put anything on the Crownstone. Make sure you call .setSettings(True, adminKey, memberKey, guesKey"
            )

        sleepTime = 0.4

        print("BluenetBLE: Starting Setup...")
        print("BluenetBLE: Setting up encryption...")
        self.handleSetupPhaseEncryption()

        try:
            print("BluenetBLE: Setting Admin Key...")
            self.writeAdminKey(self.core.settings.adminKey)
            time.sleep(sleepTime)
            print("BluenetBLE: Setting Member Key...")
            self.writeMemberKey(self.core.settings.memberKey)
            time.sleep(sleepTime)
            print("BluenetBLE: Setting Guest Key...")
            self.writeGuestKey(self.core.settings.guestKey)
            time.sleep(sleepTime)
            print("BluenetBLE: Setting Crownstone ID...")
            self.writeCrownstoneId(crownstoneId)
            time.sleep(sleepTime)
            print("BluenetBLE: Setting Mesh Access Address...")
            self.writeMeshAccessAddress(meshAccessAddress)
            time.sleep(sleepTime)
            print("BluenetBLE: Setting iBeacon UUID...")
            self.writeIBeaconUUID(ibeaconUUID)
            time.sleep(sleepTime)
            print("BluenetBLE: Setting iBeacon Major...")
            self.writeIBeaconMajor(ibeaconMajor)
            time.sleep(sleepTime)
            print("BluenetBLE: Setting iBeacon Minor...")
            self.writeIBeaconMinor(ibeaconMinor)
            time.sleep(sleepTime)
            print("BluenetBLE: Wrapping up...")
            self.validateSetup()
            time.sleep(2 * sleepTime)
            print("BluenetBLE: Setup complete!")
        except BTLEException as err:
            raise err
        except BluenetBleException as err:
            raise err
        finally:
            self.core.settings.exitSetup()
Example #14
0
 def setup(self, address, sphereId, crownstoneId, meshAccessAddress,
           meshDeviceKey, ibeaconUUID, ibeaconMajor, ibeaconMinor):
     characteristics = self.core.ble.getCharacteristics(
         CSServices.SetupService)
     try:
         self.fastSetupV2(sphereId, crownstoneId, meshDeviceKey,
                          ibeaconUUID, ibeaconMajor, ibeaconMinor)
     except BluenetBleException as e:
         if e.type is not BleError.NOTIFICATION_STREAM_TIMEOUT:
             raise e
     isNormalMode = self.core.isCrownstoneInNormalMode(
         address, 10, waitUntilInRequiredMode=True)
     if not isNormalMode:
         raise BluenetBleException(BleError.SETUP_FAILED,
                                   "The setup has failed.")
Example #15
0
 def generateIV(packetNonce, sessionData):
     if len(packetNonce) != PACKET_NONCE_LENGTH:
         raise BluenetBleException(BleError.INVALID_SESSION_NONCE, "Invalid size for session nonce packet")
     
     IV = [0] * NONCE_LENGTH
     
     # the IV used in the CTR mode is 8 bytes, the first 3 are random
     for i in range(0,PACKET_NONCE_LENGTH):
         IV[i] = packetNonce[i]
     
     # the IV used in the CTR mode is 8 bytes, the last 5 are from the session data
     for i in range(0,SESSION_DATA_LENGTH):
         IV[i + PACKET_NONCE_LENGTH] = sessionData[i]
         
     return IV
    def fastSetup(self, crownstoneId, meshAccessAddress, ibeaconUUID,
                  ibeaconMajor, ibeaconMinor):
        if not self.core.settings.initializedKeys:
            raise BluenetBleException(
                BleError.NO_ENCRYPTION_KEYS_SET,
                "Keys are not initialized so I can't put anything on the Crownstone. Make sure you call .setSettings(True, adminKey, memberKey, guesKey"
            )

        self.handleSetupPhaseEncryption()
        self.core.ble.setupNotificationStream(
            CSServices.SetupService,
            SetupCharacteristics.SetupControl, lambda: self._writeFastSetup(
                crownstoneId, meshAccessAddress, ibeaconUUID, ibeaconMajor,
                ibeaconMinor),
            lambda notificationResult: self._handleResult(notificationResult),
            5)
        print("BluenetBLE: Closing Setup.")
        self.core.settings.exitSetup()
Example #17
0
    def fastSetupV2(self, sphereId, crownstoneId, meshDeviceKey, ibeaconUUID,
                    ibeaconMajor, ibeaconMinor):
        if not self.core.settings.initializedKeys:
            raise BluenetBleException(
                BleError.NO_ENCRYPTION_KEYS_SET,
                "Keys are not initialized so I can't put anything on the Crownstone. Make sure you call .setSettings(adminKey, memberKey, basicKey, serviceDataKey, localizationKey, meshApplicationKey, meshNetworkKey"
            )

        self.handleSetupPhaseEncryption()
        self.core.ble.setupNotificationStream(
            CSServices.SetupService,
            SetupCharacteristics.Result, lambda: self._writeFastSetupV2(
                sphereId, crownstoneId, meshDeviceKey, ibeaconUUID,
                ibeaconMajor, ibeaconMinor),
            lambda notificationResult: self._handleResult(notificationResult),
            3)

        print("BluenetBLE: Closing Setup V2.")
        self.core.settings.exitSetup()
Example #18
0
    def loadSettingsFromFile(self, path):
        fileReader = JsonFileStore(path)
        data = fileReader.getData()

        if "admin" not in data:
            raise BluenetBleException(BluenetError.ADMIN_KEY_REQURED)
        if "member" not in data:
            raise BluenetBleException(BluenetError.MEMBER_KEY_REQUIRED)
        if "basic" not in data:
            raise BluenetBleException(BluenetError.BASIC_KEY_REQURED)
        if "serviceDataKey" not in data:
            raise BluenetBleException(BluenetError.SERVICE_DATA_KEY_REQUIRED)
        if "localizationKey" not in data:
            raise BluenetBleException(BluenetError.LOCALIZATION_KEY_REQUIRED)
        if "meshApplicationKey" not in data:
            raise BluenetBleException(BluenetError.MESH_APP_KEY)
        if "meshNetworkKey" not in data:
            raise BluenetBleException(BluenetError.MESH_NETWORK_KEY)

        self.setSettings(data["admin"], data["member"], data["basic"],
                         data["serviceDataKey"], data["localizationKey"],
                         data["meshApplicationKey"], data["meshNetworkKey"])