Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
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
Beispiel #7
0
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