def _process_management_key_additions( entry_content, signing_key_required_priority, new_keys, active_keys, all_keys, chain_id, network, ): for key_data in entry_content["add"].get("managementKey", []): alias = _get_alias(key_data["id"]) if ( not validate_management_key_id_against_chain_id(key_data["id"], chain_id) or not validate_id_against_network(key_data["id"], network) or alias in new_keys or alias in active_keys ): return True, signing_key_required_priority new_management_key = ManagementKey.from_entry_dict(key_data) if new_management_key in all_keys: return True, signing_key_required_priority new_keys[alias] = new_management_key signing_key_required_priority = min( signing_key_required_priority, key_data["priority"] ) return False, signing_key_required_priority
def _process_service_revocations( entry_content, signing_key_required_priority, services_to_revoke, active_services, network, ): for service in entry_content["revoke"].get("service", []): alias = _get_alias(service["id"]) # If: # * revocation of a non-existent service, or # * multiple revocations of the same service, or # * revocations of a service with a non-matching network identifier # are attempted ignore the entire DIDUpdate entry if ( alias not in active_services or alias in services_to_revoke or not validate_id_against_network(service["id"], network) ): return True, signing_key_required_priority services_to_revoke.add(alias) if active_services[alias].priority_requirement is not None: signing_key_required_priority = min( signing_key_required_priority, active_services[alias].priority_requirement, ) return False, signing_key_required_priority
def _process_management_key_revocations( entry_content, signing_key_required_priority, keys_to_revoke, active_keys, chain_id, network, ): for key in entry_content["revoke"].get("managementKey", []): alias = _get_alias(key["id"]) if ( not validate_management_key_id_against_chain_id(key["id"], chain_id) or not validate_id_against_network(key["id"], network) or alias not in active_keys or alias in keys_to_revoke ): return True, signing_key_required_priority keys_to_revoke.add(alias) if active_keys[alias].priority_requirement is not None: signing_key_required_priority = min( signing_key_required_priority, active_keys[alias].priority_requirement ) else: signing_key_required_priority = min( signing_key_required_priority, active_keys[alias].priority ) return False, signing_key_required_priority
def _process_did_key_revocations( entry_content, signing_key_required_priority, keys_to_revoke, key_purposes_to_revoke, active_keys, network, ): for key_data in entry_content["revoke"].get("didKey", []): alias = _get_alias(key_data["id"]) # If: # * revocation of a non-existent key, or # * multiple revocations of the same key, or # * revocations of a DID key with a non-matching network identifier # are attempted ignore the entire DIDUpdate entry if ( alias not in active_keys or alias in keys_to_revoke or not validate_id_against_network(key_data["id"], network) ): return True, signing_key_required_priority if "purpose" in key_data: purposes = key_data["purpose"] # If duplicate purposes are specified, ignore the entry if len(purposes) != len(set(purposes)): return True, signing_key_required_priority active_purposes = set(map(lambda p: p.value, active_keys[alias].purpose)) valid_purposes = { DIDKeyPurpose.AuthenticationKey.value, DIDKeyPurpose.PublicKey.value, } for purpose in purposes: if purpose not in valid_purposes or purpose not in active_purposes: return True, signing_key_required_priority # If all purposes are revoked, revoke the entire key if set(purposes) == active_purposes: keys_to_revoke.add(alias) # Otherwise, just revoke the specific purpose. Note, that due to the checks above, we should be guaranteed # that only a single purpose is being revoked else: assert len(purposes) == 1 key_purposes_to_revoke[alias] = DIDKeyPurpose.from_str(purposes[0]) else: if alias in key_purposes_to_revoke: del key_purposes_to_revoke[alias] keys_to_revoke.add(alias) if active_keys[alias].priority_requirement is not None: signing_key_required_priority = min( signing_key_required_priority, active_keys[alias].priority_requirement ) return False, signing_key_required_priority
def _process_service_additions(entry_content, new_services, active_services, network): for service_data in entry_content["add"].get("service", []): alias = _get_alias(service_data["id"]) # If double-addition of the same service or addition of a service with a non-matching network identifier # is attempted, ignore the entire DIDUpdate entry if ( alias in new_services or alias in active_services or not validate_id_against_network(service_data["id"], network) ): return True new_services[alias] = Service.from_entry_dict(service_data) return False
def _process_did_key_additions( entry_content, signing_key_required_priority, new_keys, active_keys, all_keys, network, ): for key_data in entry_content["add"].get("didKey", []): alias = _get_alias(key_data["id"]) # If double-addition of the same key or addition of a key with a non-matching network identifier is # attempted, ignore the entire DIDUpdate entry if ( alias in new_keys or alias in active_keys or not validate_id_against_network(key_data["id"], network) ): return True, signing_key_required_priority new_did_key = DIDKey.from_entry_dict(key_data) if new_did_key in all_keys: return True, signing_key_required_priority new_keys[alias] = new_did_key return False, signing_key_required_priority
def process_did_management_entry_v100( chain_id, parsed_content, management_keys, did_keys, services, skipped_entries, network, ): """ Extracts the management keys, DID keys and services from a DIDManagement entry. This method only does validation of the logic rules for a DIDManagement entry (e.g. that at least one management key with priority 0 is present). Thus, it must be called only with a parsed entry, which has already undergone validation checks for proper formatting of its ExtIDs and content. Parameters ---------- chain_id: str The DIDManagement chain ID. parsed_content: dict The parsed DIDManagement entry. management_keys: dict Will be updated to contain the management keys found in the entry. did_keys: dict Will be updated to contain the DID keys found in the entry. services: dict Will be updated to contain the services found in the entry. skipped_entries: int Will be incremented by one in case the DIDManagement entry is not valid. network: Network The Factom network on which the DID is recorded Returns ------- tuple 3-tuple (bool, str, int). The first element signifies if the caller should continue parsing the chain; the second element contains the current DID method specification version; the third element contains the number of skipped entries in the DIDManagement chain. Raises ------ MalformedDIDManagementEntry If the DIDManagement entry does not conform to the DID specification """ # Store the new management_keys, did_keys and services in separate objects, instead of # modifying the original ones directly. This ensures that if an exception occurs during # the processing of the entry, the original values will not be modified. new_management_keys = {} new_did_keys = {} new_services = {} method_version = parsed_content["didMethodVersion"] found_key_with_priority_zero = False for key_data in parsed_content["managementKey"]: if not validate_management_key_id_against_chain_id(key_data["id"], chain_id): raise MalformedDIDManagementEntry( "Invalid key identifier '{}' for chain ID '{}'".format( key_data["id"], chain_id ) ) if not validate_id_against_network(key_data["id"], network): raise MalformedDIDManagementEntry( "Invalid key identifier '{}' for network ID '{}'".format( key_data["id"], network.value ) ) alias = _get_alias(key_data["id"]) if alias in new_management_keys: raise MalformedDIDManagementEntry("Duplicate management key found") new_management_keys[alias] = ManagementKey.from_entry_dict(key_data) if key_data["priority"] == 0: found_key_with_priority_zero = True if not found_key_with_priority_zero: raise MalformedDIDManagementEntry( "Entry must contain at least one management key with priority 0" ) for key_data in parsed_content.get("didKey", []): if not validate_id_against_network(key_data["id"], network): raise MalformedDIDManagementEntry( "Invalid key identifier '{}' for network ID '{}'".format( key_data["id"], network.value ) ) alias = _get_alias(key_data["id"]) if alias in new_did_keys: raise MalformedDIDManagementEntry("Duplicate DID key found") new_did_keys[alias] = DIDKey.from_entry_dict(key_data) for service_data in parsed_content.get("service", []): if not validate_id_against_network(service_data["id"], network): raise MalformedDIDManagementEntry( "Invalid service identifier '{}' for network ID '{}'".format( service_data["id"], network.value ) ) alias = _get_alias(service_data["id"]) if alias in new_services: raise MalformedDIDManagementEntry("Duplicate service found") new_services[alias] = Service.from_entry_dict(service_data) # Only change the original keys & services if the processing of the whole entry has been successful management_keys.update(new_management_keys) did_keys.update(new_did_keys) services.update(new_services) return True, method_version, skipped_entries