def validate(self): """ Ensure the condition is valid according the local rules. Checks the condition against the local bitmask (supported condition types) and the local maximum fulfillment size. Returns: bool: Whether the condition is valid according to local rules. """ # Get class for type ID, throws on error TypeRegistry.get_class_from_type_id(self.type_id) # Bitmask can have at most 32 bits with current implementation if self.bitmask > Condition.MAX_SAFE_BITMASK: raise ValueError('Bitmask too large to be safely represented') # Assert all requested features are supported by this implementation if self.bitmask & ~Condition.SUPPORTED_BITMASK: raise ValueError('Condition requested unsupported feature suites') # Assert the requested fulfillment size is supported by this implementation if self.max_fulfillment_length > Condition.MAX_FULFILLMENT_LENGTH: raise ValueError('Condition requested too large of a max fulfillment size') return True
def validate(self): """ Ensure the condition is valid according the local rules. Checks the condition against the local bitmask (supported condition types) and the local maximum fulfillment size. Returns: bool: Whether the condition is valid according to local rules. """ # Get class for type ID, throws on error TypeRegistry.get_class_from_type_id(self.type_id) # Bitmask can have at most 32 bits with current implementation if self.bitmask > Condition.MAX_SAFE_BITMASK: raise ValueError('Bitmask too large to be safely represented') # Assert all requested features are supported by this implementation if self.bitmask & ~Condition.SUPPORTED_BITMASK: raise ValueError('Condition requested unsupported feature suites') # Assert the requested fulfillment size is supported by this implementation if self.max_fulfillment_length > Condition.MAX_FULFILLMENT_LENGTH: raise ValueError( 'Condition requested too large of a max fulfillment size') return True
def validate(self): """ Ensure the condition is valid according the local rules. Checks the condition against the local subtypes (supported condition types) and the local maximum fulfillment size. Returns: bool: Whether the condition is valid according to local rules. """ # Get class for type ID, throws on error TypeRegistry.find_by_type_id(self.type_id) # Subtypes can have at most 32 bits with current implementation if len(self.subtypes) > Condition.MAX_SAFE_SUBTYPES: raise ValueError('Subtypes too large to be safely represented') # Assert all requested features are supported by this implementation if any(subtype not in Condition.SUPPORTED_SUBTYPES for subtype in self.subtypes): raise ValueError('Condition requested unsupported feature suites') # Assert the requested fulfillment size # is supported by this implementation if self.cost > Condition.MAX_COST: raise ValueError( 'Condition requested too large of a max fulfillment size') return True
def to_asn1_dict(self): condition_type = TypeRegistry.find_by_type_id(self.type_id) condition_class = condition_type['class'] payload = {'fingerprint': self.hash, 'cost': self.cost} if condition_class.TYPE_CATEGORY == 'compound': subtype_ids = [ TypeRegistry.find_by_name(subtype)['type_id'] for subtype in self.subtypes ] bits = ['0' for bit in range(5)] for subtype_id in subtype_ids: bits[subtype_id] = '1' bitstring = ''.join(bits).rstrip('0') payload['subtypes'] = bitstring return {condition_class.TYPE_ASN1: payload}
def from_uri(serialized_fulfillment): """ Create a Fulfillment object from a URI. This method will parse a fulfillment URI and construct a corresponding Fulfillment object. Args: serialized_fulfillment (str): URI representing the fulfillment Return: Fulfillment: Resulting object """ if isinstance(serialized_fulfillment, Fulfillment): return serialized_fulfillment elif not isinstance(serialized_fulfillment, str): raise TypeError('Serialized fulfillment must be a string') pieces = serialized_fulfillment.split(':') if not pieces[0] == 'cf': raise ValueError('Serialized fulfillment must start with "cf:"') if not re.match(Fulfillment.REGEX, serialized_fulfillment): raise ValueError('Invalid fulfillment format') # try: type_id = int(pieces[1], 16) payload = base64.urlsafe_b64decode(base64_add_padding(pieces[2])) cls = TypeRegistry.get_class_from_type_id(type_id) fulfillment = cls() fulfillment.parse_payload(Reader.from_source(payload), len(payload)) # except Exception as e: # raise ParsingError(str(e)) return fulfillment
def from_uri(serialized_condition): """ Create a Condition object from a URI. This method will parse a condition URI and construct a corresponding Condition object. Args: serialized_condition (str): URI representing the condition Returns: Condition: Resulting object """ # TODO consider removing. if isinstance(serialized_condition, Condition): return serialized_condition # TODO Use static typing instead (e.g.: with mypy). elif not isinstance(serialized_condition, str): raise TypeError('Serialized condition must be a string') pieces = serialized_condition.split(':') if pieces[0] != CONDITION_URI_SCHEME: raise PrefixError( 'Serialized condition must start with "{}:"'.format( CONDITION_URI_SCHEME ) ) regex_match = re.match(CONDITION_REGEX_STRICT, serialized_condition) if not regex_match: raise ParsingError('Invalid condition format') qs_dict = parse_qs(regex_match.group(2)) try: fingerprint_type = qs_dict['fpt'][0] except (KeyError, IndexError): raise ParsingError( 'Invalid condition format: "fpt" parameter or value missing.') condition_type = TypeRegistry.find_by_name(fingerprint_type) try: cost = qs_dict['cost'][0] except (KeyError, IndexError): raise ParsingError( 'Invalid condition format: "cost" parameter or value missing.') if not re.match(INTEGER_REGEX, cost): raise ParsingError('No or invalid cost provided') fingerprint = regex_match.group(1) condition = Condition() condition.type_id = condition_type['type_id'] condition._subtypes = set() if condition_type['class'].TYPE_CATEGORY == 'compound': condition._subtypes.update(qs_dict['subtypes'][0].split(',')) condition.hash = base64.urlsafe_b64decode( base64_add_padding(fingerprint)) condition.cost = int(cost) return condition
def from_dict(data): cls_type = data['type_id'] cls = TypeRegistry.get_class_from_type_id(cls_type) fulfillment = cls() fulfillment.parse_dict(data) return fulfillment
def from_asn1_dict(asn1_dict): asn1_type, value = asn1_dict.popitem() registered_type = TypeRegistry.find_by_asn1_type(asn1_type) # Instantiate condition condition = Condition() condition.type_id = registered_type['type_id'] condition.hash = value['fingerprint'] condition.cost = value['cost'] condition._subtypes = set() if registered_type['class'].TYPE_CATEGORY == 'compound': subtypes = { TypeRegistry.find_by_type_id(type_id)['name'] for type_id in compress( range(Condition.MAX_SAFE_SUBTYPES), map(lambda bit: int(bit), value['subtypes']) ) } condition._subtypes.update(subtypes) return condition
def serialize_uri(self): """ Generate the URI form encoding of this condition. Turns the condition into a URI containing only URL-safe characters. This format is convenient for passing around conditions in URLs, JSON and other text-based formats. "cc:" BASE16(TYPE_ID) ":" BASE16(BITMASK) ":" BASE64URL(HASH) ":" BASE10(MAX_COST) Returns: string: Condition as a URI """ condition_type = TypeRegistry.find_by_type_id(self.type_id) condition_class = TypeRegistry.find_by_type_id(self.type_id)['class'] include_subtypes = condition_class.TYPE_CATEGORY == 'compound' uri = 'ni:///sha-256;{}?fpt={}&cost={}'.format( base64_remove_padding( base64.urlsafe_b64encode(self.hash)).decode(), condition_type['name'], self.cost, ) if include_subtypes: uri += '&subtypes=' + ','.join(sorted(self.subtypes)) return uri
def from_binary(reader): """ Create a Fulfillment object from a binary blob. This method will parse a stream of binary data and construct a corresponding Fulfillment object. Args: reader (Reader): Binary stream implementing the Reader interface Returns: Fulfillment: Resulting object """ reader = Reader.from_source(reader) cls_type = reader.read_uint16() cls = TypeRegistry.get_class_from_type_id(cls_type) fulfillment = cls() payload_length = reader.read_length_prefix() fulfillment.parse_payload(reader, payload_length) return fulfillment
def from_json(data): type_ = TypeRegistry.find_by_name(data['type']) fulfillment = type_['class']() fulfillment.parse_json(data) return fulfillment
def from_asn1_dict(asn1_dict): asn1_type, value = asn1_dict.popitem() instance = TypeRegistry.find_by_asn1_type(asn1_type)['class']() instance.parse_asn1_dict_payload(value) instance.asn1_dict = {asn1_type: value} return instance
def to_asn1_json(self): asn1_type, value = self.to_asn1_dict().popitem() condition_type = TypeRegistry.find_by_asn1_type(asn1_type) return {'type': condition_type['asn1_condition'], 'value': value}
def type_name(self): return TypeRegistry.find_by_type_id(self.type_id)['name']