def _send_request(self,
                      suffix,
                      data=None,
                      contentType=None):
        if self.url.startswith("http://"):
            url = "{}/{}".format(self.url, suffix)
        else:
            url = "http://{}/{}".format(self.url, suffix)

        headers = {}

        if contentType is not None:
            headers['Content-Type'] = contentType

        try:
            if data is not None:
                result = requests.post(url, headers=headers, data=data)
            else:
                result = requests.get(url, headers=headers)

            if not result.ok:
                raise CapBACClientException("Error {}: {}".format(
                    result.status_code, result.reason))

        except requests.ConnectionError as err:
            raise CapBACClientException(
                'Failed to connect to {}: {}'.format(url, str(err)))

        except BaseException as err:
            raise CapBACClientException(err)

        return result.text
    def list(self,device):

        if len(device) > MAX_URI_LENGTH:
            raise CapBACClientException(
                'Invalid URI: max length exceeded, should be less than {}'
                .format(MAX_URI_LENGTH))

        result = self._send_request(
            "state?address={}".format(
                self._get_address(device)))

        try:
            encoded_entries = yaml.safe_load(result)["data"]

            data_list = [
                cbor.loads(base64.b64decode(entry["data"]))
                for entry in encoded_entries
            ]

            return json.dumps({
                x:y[x] for y in data_list for x in y
            }, indent=4, sort_keys=True)

        except BaseException:
            return None
    def validate(self,token):

        try:
            token = json.loads(token)
        except:
            raise CapBACClientException('Invalid access token: serialization failed')

        return self.validate_from_dict(token)
    def revoke(self, token):

        try:
            token = json.loads(token)
        except:
            raise CapBACClientException('Invalid revocation token: serialization failed')

        return self.revoke_from_dict(token)
    def issue(self, token, is_root):

        try:
            token = json.loads(token)
        except:
            raise CapBACClientException('Invalid token: serialization failed')

        return self.issue_from_dict(token, is_root)
def _check_format(dictionary,name,dictionary_format,subset=None):
    if subset is None:
        subset = set(dictionary_format)
    for label in subset:
        if label not in dictionary:
            raise CapBACClientException("Invalid {}: {} missing ({})"
            .format(name,label,dictionary_format[label]['description']))
        feature = dictionary[label]
        if 'allowed values' in dictionary_format[label]:
            if feature not in dictionary_format[label]['allowed values']:
                raise CapBACClientException(
                "Invalid {}: {} value should be one the following: {}"
                .format(name,label,dictionary_format[label]['allowed values']))
        elif 'allowed types' in dictionary_format[label]:
            if type(feature) not in dictionary_format[label]['allowed types']:
                raise CapBACClientException(
                "Invalid {}: {} type not allowed".format(name,label))
        elif type(feature) == str: # string allowed by default
            if 'len' in dictionary_format[label]:
                if len(feature) != dictionary_format[label]['len']:
                    raise CapBACClientException(
                        "Invalid {}: {} length should be {}"
                        .format(name,label,dictionary_format[label]['len']))
            elif 'max_len' in dictionary_format[label]:
                if len(feature) > dictionary_format[label]['max_len']:
                    raise CapBACClientException(
                        "Invalid {}: {} length should less than {}"
                        .format(name,label,dictionary_format[label]['max_len']))
        else:
            raise CapBACClientException(
                "Invalid {}: {} should be a string".format(name,label))
    for label in dictionary:
        if label not in subset:
            raise CapBACClientException("Invalid {}: unexpected label {}".format(name,label))
    def sign(self, token):

        try:
            token = json.loads(token)
        except:
            raise CapBACClientException('Invalid token: serialization failed')

        token = self.sign_dict(token)
        return json.dumps(token)
    def __init__(self, url, keyfile=None):
        self.url = url

        if keyfile is not None:
            try:
                with open(keyfile) as fd:
                    private_key_str = fd.read().strip()
                    fd.close()
            except OSError as err:
                raise CapBACClientException(
                    'Failed to read private key: {}'.format(str(err)))

            try:
                private_key = Secp256k1PrivateKey.from_hex(private_key_str)
            except ParseError as e:
                raise CapBACClientException(
                    'Unable to load private key: {}'.format(str(e)))

            self._signer = CryptoFactory(
                create_context('secp256k1')).new_signer(private_key)
    def issue_from_dict(self,token, is_root):

        # check the formal validity of the incomplete token
        subset = set(CAPABILITY_FORMAT) - {'II','SI','VR'}
        if is_root: subset -= {'IC','SU'}

        _check_format(token,'capabiliy token',CAPABILITY_FORMAT,subset)

        for access_right in token['AR']:
            _check_format(access_right,'capability token: access right',ACCESS_RIGHT_FORMAT)

        # time interval logical check
        try:
            not_before = int(token['NB'])
            not_after  = int(token['NA'])
        except:
            raise CapBACClientException('Invalid capability: timestamp not a number')

        if not_before > not_after:
            raise CapBACClientException("Invalid capability: incorrect time interval")

        now = int(time.time())
        if now > not_after:
            raise CapBACClientException("Capability already expired")

        if is_root:
            token['IC'] = None
            token['SU'] = self._signer.get_public_key().as_hex()

        # add signature
        token= self.sign_dict(token)

        # now the token is complete

        payload = cbor.dumps({
            'AC': "issue",
            'OB': token
        })

        return self._send_transaction(payload, token['DE'])