Exemple #1
0
    def test_invalid_entry(self):
        chain_id = secrets.token_hex(32)
        did = "{}:{}".format(DID_METHOD_NAME, chain_id)

        # Entry with invalid property names
        with pytest.raises(ValidationError):
            self.VALIDATOR.validate({
                "added": {
                    "managementKey": [
                        ManagementKey(
                            alias="my-man-key-1",
                            controller=did,
                            key_type=KeyType.EdDSA,
                            priority=0,
                        ).to_entry_dict(did)
                    ]
                }
            })

        # Entry with invalid property names
        with pytest.raises(ValidationError):
            self.VALIDATOR.validate({
                "add": {
                    "managementKey": [
                        ManagementKey(
                            alias="my-man-key-1",
                            controller=did,
                            key_type=KeyType.EdDSA,
                            priority=0,
                        ).to_entry_dict(did)
                    ]
                },
                "remove": {
                    "managementKey": [{
                        "id": "management-1"
                    }]
                },
            })

        # Entry with additional properties
        with pytest.raises(ValidationError):
            self.VALIDATOR.validate({
                "add": {
                    "managementKey": [
                        ManagementKey(
                            alias="my-man-key-1",
                            controller=did,
                            key_type=KeyType.EdDSA,
                            priority=0,
                        ).to_entry_dict(did)
                    ]
                },
                "revoke": {
                    "managementKey": [{
                        "id": "management-1"
                    }]
                },
                "additional": {},
            })
Exemple #2
0
 def test_equality(self, controller):
     private_key = secrets.token_bytes(32)
     key1 = AbstractDIDKey(
         alias="test-key",
         key_type=KeyType.EdDSA,
         controller=controller,
         priority_requirement=1,
         private_key=private_key,
     )
     key2 = AbstractDIDKey(
         alias="test-key",
         key_type=KeyType.EdDSA,
         controller=controller,
         priority_requirement=1,
         private_key=private_key,
     )
     key3 = ManagementKey(
         alias="test-key",
         key_type=KeyType.EdDSA,
         controller=controller,
         priority_requirement=1,
         priority=0,
         private_key=private_key,
     )
     key4 = AbstractDIDKey(
         alias="test-key-2",
         key_type=KeyType.EdDSA,
         controller=controller,
         priority_requirement=1,
         private_key=private_key,
     )
     assert key1 == key2
     assert key1.__eq__(key3) == NotImplemented
     assert key1 != key4
Exemple #3
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
Exemple #4
0
 def test_valid_entry(self):
     did = "{}:{}".format(DID_METHOD_NAME, secrets.token_hex(32))
     validate_did_management_ext_ids_v100([b"DIDManagement", b"1.0.0"])
     validate_did_management_ext_ids_v100(
         [b"DIDManagement", b"1.0.0", b"asdfasdfasdf"])
     valid_entry = {
         "didMethodVersion":
         "0.2.0",
         "managementKey": [
             ManagementKey(
                 alias="my-man-key-1",
                 controller=did,
                 key_type=KeyType.EdDSA,
                 priority=0,
             ).to_entry_dict(did),
             ManagementKey(
                 alias="my-man-key-2",
                 controller=did,
                 key_type=KeyType.RSA,
                 priority=1,
             ).to_entry_dict(did),
         ],
         "didKey": [
             DIDKey(
                 alias="my-did-key",
                 controller=did,
                 key_type=KeyType.RSA.ECDSA,
                 purpose=DIDKeyPurpose.PublicKey,
             ).to_entry_dict(did)
         ],
         "service": [
             Service(
                 alias="gmail-service",
                 service_type="email-service",
                 endpoint="https://gmail.com",
                 priority_requirement=1,
                 custom_fields={
                     "description": "My email service"
                 },
             ).to_entry_dict(did)
         ],
     }
     self.VALIDATOR.validate(valid_entry)
Exemple #5
0
    def test_entry_with_invalid_did_method_version(self):
        did = "{}:{}".format(DID_METHOD_NAME, secrets.token_hex(32))
        entry = {
            "didMethodVersion":
            "0.3.0",
            "managementKey": [
                ManagementKey(
                    alias="my-man-key-1",
                    controller=did,
                    key_type=KeyType.EdDSA,
                    priority=0,
                ).to_entry_dict(did),
                ManagementKey(
                    alias="my-man-key-2",
                    controller=did,
                    key_type=KeyType.RSA,
                    priority=1,
                ).to_entry_dict(did),
            ],
        }

        with pytest.raises(ValidationError):
            self.VALIDATOR.validate(entry)
Exemple #6
0
    def test_entry_with_missing_required_fields(self):
        with pytest.raises(ValidationError):
            self.VALIDATOR.validate({})

        missing_management_keys = json.dumps({"didMethodVersion": "0.2.0"})
        with pytest.raises(ValidationError):
            self.VALIDATOR.validate(missing_management_keys)

        did = "{}:{}".format(DID_METHOD_NAME, secrets.token_hex(32))
        missing_did_method_version = {
            "managementKey": [
                ManagementKey(
                    alias="my-man-key",
                    controller=did,
                    key_type=KeyType.EdDSA,
                    priority=0,
                ).to_entry_dict(did)
            ]
        }
        with pytest.raises(ValidationError):
            self.VALIDATOR.validate(missing_did_method_version)
Exemple #7
0
    def management_key(
        self,
        alias,
        priority,
        key_type=KeyType.EdDSA,
        controller=None,
        priority_requirement=None,
    ):
        """
        Creates a new management key for the DID.

        Parameters
        ----------
        alias: str
            A human-readable nickname for the key. It should be unique across the keys defined in the DID document.
        priority: int
            A non-negative integer showing the hierarchical level of the key. Keys with lower priority
            override keys with higher priority.
        key_type: KeyType, optional
            Identifies the type of signature that the key pair can be used to generate and verify.
        controller: str, optional
            An entity that controls the key. It must be a valid DID. If the argument is not passed in,
            the default value is used which is the current DID itself.
        priority_requirement: int, optional
            A non-negative integer showing the minimum hierarchical level a key must have in order to remove this key.
        """

        if not controller:
            controller = self.id

        key = ManagementKey(alias, priority, key_type, controller,
                            priority_requirement)
        self._check_alias_is_unique_and_add_to_used(self.used_key_aliases,
                                                    alias)

        self.management_keys.append(key)

        return self
Exemple #8
0
    def test_valid_entry(self):
        chain_id = secrets.token_hex(32)
        did = "{}:{}".format(DID_METHOD_NAME, chain_id)
        key_id = "{}#{}".format(did, "my-man-key-1")

        validate_did_update_ext_ids_v100(
            [b"DIDUpdate", b"1.0.0",
             key_id.encode("utf-8"), b"affe"], chain_id)

        # Entry with only additions should be valid
        self.VALIDATOR.validate({
            "add": {
                "managementKey": [
                    ManagementKey(
                        alias="my-man-key-1",
                        controller=did,
                        key_type=KeyType.EdDSA,
                        priority=0,
                    ).to_entry_dict(did),
                    ManagementKey(
                        alias="my-man-key-2",
                        controller=did,
                        key_type=KeyType.RSA,
                        priority=1,
                    ).to_entry_dict(did),
                ],
                "didKey": [
                    DIDKey(
                        alias="my-did-key",
                        controller=did,
                        key_type=KeyType.RSA.ECDSA,
                        purpose=DIDKeyPurpose.PublicKey,
                    ).to_entry_dict(did)
                ],
            }
        })

        # Entry with only revocations should be valid
        self.VALIDATOR.validate({
            "revoke": {
                "managementKey": [{
                    "id": "management-key-1"
                }],
                "didKey": [
                    {
                        "id": "did-key-1"
                    },
                    {
                        "id": "did-key-2",
                        "purpose": ["authentication"]
                    },
                ],
                "service": [{
                    "id": "service-1"
                }],
            }
        })

        # Entry with both additions and revocations should be valid
        self.VALIDATOR.validate({
            "add": {
                "managementKey": [
                    ManagementKey(
                        alias="my-man-key-1",
                        controller=did,
                        key_type=KeyType.EdDSA,
                        priority=0,
                    ).to_entry_dict(did),
                    ManagementKey(
                        alias="my-man-key-2",
                        controller=did,
                        key_type=KeyType.RSA,
                        priority=1,
                    ).to_entry_dict(did),
                ],
                "didKey": [
                    DIDKey(
                        alias="my-did-key",
                        controller=did,
                        key_type=KeyType.RSA.ECDSA,
                        purpose=DIDKeyPurpose.PublicKey,
                    ).to_entry_dict(did)
                ],
                "service": [
                    Service(
                        alias="gmail-service",
                        service_type="email-service",
                        endpoint="https://gmail.com",
                        priority_requirement=1,
                        custom_fields={
                            "description": "My email service"
                        },
                    ).to_entry_dict(did)
                ],
            },
            "revoke": {
                "managementKey": [{
                    "id": "management-key-1"
                }],
                "didKey": [
                    {
                        "id": "did-key-1"
                    },
                    {
                        "id": "did-key-2",
                        "purpose": ["publicKey"]
                    },
                ],
                "service": [{
                    "id": "service-1"
                }],
            },
        })
Exemple #9
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