Exemple #1
0
    def __init__(self,
                 did: str,
                 ident: str,
                 typ: str,
                 recip_keys: Union[Sequence, PublicKey],
                 routing_keys: Union[Sequence, PublicKey],
                 endpoint: str,
                 priority: int = 0):
        """
        Retain service specification particulars. Raise BadIdentifier on bad input controller DID.

        :param did: DID of DID document embedding service, specified raw (operation converts to URI)
        :param ident: identifier for service
        :param typ: service type
        :param recip_keys: recipient key or keys
        :param routing_keys: routing key or keys
        :param endpoint: service endpoint
        :param priority: service priority
        """

        self._did = canon_did(did)
        self._id = canon_ref(self._did, ident, ';')
        self._type = typ
        self._recip_keys = ([recip_keys] if isinstance(recip_keys, PublicKey)
                            else list(recip_keys) if recip_keys else None)
        self._routing_keys = ([routing_keys] if isinstance(
            routing_keys, PublicKey) else
                              list(routing_keys) if routing_keys else None)
        self._endpoint = endpoint
        self._priority = priority
Exemple #2
0
    def __init__(
            self,
            did: str,
            ident: str,
            value: str,
            pk_type: PublicKeyType = None,
            controller: str = None,
            authn: bool = False) -> None:
        """
        Retain key specification particulars. Raise BadIdentifier on any bad input DID.

        :param did: DID of DID document embedding public key
        :param ident: identifier for public key
        :param value: key content, encoded as key specification requires
        :param pk_type: public key type (enum), default ED25519_SIG_2018
        :param controller: controller DID (default DID of DID document)
        :param authn: whether key as has DID authentication privilege (default False)
        """

        self._did = canon_did(did)
        self._id = canon_ref(self._did, ident)
        self._value = value
        self._type = pk_type or PublicKeyType.ED25519_SIG_2018
        self._controller = canon_did(controller) if controller else self._did
        self._authn = authn
Exemple #3
0
    def to_dict(self):
        """
        Return dict representation of service to embed in DID document.
        """

        rv = {'id': self.id, 'type': self.type, 'priority': self.priority}
        if self.recip_keys:
            rv['routingKeys'] = [
                canon_ref(k.did, k.id, '#') for k in self.recip_keys
            ]
        if self.routing_keys:
            rv['routingKeys'] = [
                canon_ref(k.did, k.id, '#') for k in self.routing_keys
            ]
        rv['serviceEndpoint'] = self.endpoint

        return rv
Exemple #4
0
    def serialize(self) -> str:
        """
        Dump current object to a JSON-compatible dictionary.

        :return: dict representation of current DIDDoc
        """

        return {
            '@context':
            DIDDoc.CONTEXT,
            'id':
            canon_ref(self.did, self.did),
            'publicKey': [pubkey.to_dict() for pubkey in self.pubkey.values()],
            'authentication': [{
                'type': pubkey.type.authn_type,
                'publicKey': canon_ref(self.did, pubkey.id)
            } for pubkey in self.pubkey.values() if pubkey.authn],
            'service':
            [service.to_dict() for service in self.service.values()]
        }
Exemple #5
0
    def serialize(self, service_key_refs: bool = False) -> str:
        """
        Dump current object to a JSON-compatible dictionary.

        :param service_key_refs: whether to output service keys as references
            rather than raw
        :return: dict representation of current DIDDoc
        """

        return {
            '@context': DIDDoc.CONTEXT,
            'id': canon_ref(self.did, self.did),
            'publicKey': [pubkey.to_dict() for pubkey in self.pubkey.values()],
            'authentication': [
                canon_ref(self.did, pubkey.id) for pubkey in self.pubkey.values() if pubkey.authn
            ],
            'service': [
                service.to_dict(service_key_refs) for service in self.service.values()
            ]
        }
Exemple #6
0
    def to_dict(self):
        """
        Return dict representation of public key to embed in DID document.
        """

        return {
            'id': self.id,
            'type': str(self.type.ver_type),
            'controller': canon_ref(self.did, self.controller),
            **self.type.specification(self.value)
        }
Exemple #7
0
    def to_dict(self, key_refs: bool = False) -> dict:
        """
        Return dict representation of service to embed in DID document.

        :param key_refs: whether to output keys as references rather than raw

        :return: dict representation of service to embed in DID document
        """

        rv = {
            'id': self.id,
            'type': self.type,
            'priority': self.priority
        }
        if self.recip_keys:
            rv['recipientKeys'] = [canon_ref(k.did, k.id, '#') if key_refs else k.value for k in self.recip_keys]
        if self.routing_keys:
            rv['routingKeys'] = [canon_ref(k.did, k.id, '#') if key_refs else k.value for k in self.routing_keys]
        rv['serviceEndpoint'] = self.endpoint

        return rv
Exemple #8
0
    def add_service_pubkeys(
            self, service: dict, tags: Union[Sequence[str],
                                             str]) -> List[PublicKey]:
        """
        Add public keys specified in service. Return public keys so discovered.

        Raise AbsentDIDDocItem for public key reference not present in DID document.

        :param service: service from DID document
        :param tags: potential tags marking public keys of type of interest - the standard is still coalescing
        :return: list of public keys that service specification in DID document identifies.
        """

        rv = []
        for tag in [tags] if isinstance(tags, str) else list(tags):

            for svc_key in service.get(tag, {}):
                canon_key = canon_ref(self.did, svc_key)
                pubkey = None

                if '#' in svc_key:
                    if canon_key in self.pubkey:
                        pubkey = self.pubkey[canon_key]
                    else:  # service key refers to another DID doc
                        LOGGER.debug(
                            'DIDDoc.add_service_pubkeys <!< DID document %s has no public key %s',
                            self.did, svc_key)
                        raise AbsentDIDDocItem(
                            'DID document {} has no public key {}'.format(
                                self.did, svc_key))
                else:
                    for existing_pubkey in self.pubkey.values():
                        if existing_pubkey.value == svc_key:
                            pubkey = existing_pubkey
                            break
                    else:
                        pubkey = PublicKey(
                            self.did,
                            ident=svc_key[
                                -9:-1],  # industrial-grade uniqueness
                            value=svc_key)
                        self._pubkey[pubkey.id] = pubkey

                if pubkey and pubkey not in rv:  # perverse case: could specify same key multiple ways; append once
                    rv.append(pubkey)

        return rv
Exemple #9
0
async def test_a2a():
    print(Ink.YELLOW('\n\n== Testing DID Doc wranglers =='))

    # One authn key by reference
    dd_in = {
        '@context': 'https://w3id.org/did/v1',
        'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
        'publicKey': [
            {
                'id': '3',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC X...'
            },
            {
                'id': '4',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC 9...'
            },
            {
                'id': '6',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC A...'
            }
        ],
        'authentication': [
            {
                'type': 'RsaSignatureAuthentication2018',
                'publicKey': 'did:sov:LjgpST2rjsoxYegQDRm7EL#4'
            }
        ],
        'service': [
            {
                'id': '0',
                'type': 'Agency',
                'serviceEndpoint': 'did:sov:Q4zqM7aXqm7gDQkUVLng9h'
            }
        ]
    }

    dd = DIDDoc.deserialize(dd_in)
    assert len(dd.pubkey) == len(dd_in['publicKey'])
    assert len(dd.authnkey) == len(dd_in['authentication'])

    dd_out = dd.serialize()
    print('\n\n== 1 == DID Doc {} on abbreviated identifiers: {}'.format(dd, ppjson(dd_out)))

    # Exercise JSON, de/serialization
    dd_json = dd.to_json()
    dd_copy = dd.from_json(dd_json)
    assert dd_copy.did == dd.did
    assert all(dd_copy.authnkey[k].to_dict() == dd.authnkey[k].to_dict() for k in dd_copy.authnkey)
    assert {k for k in dd_copy.authnkey} == {k for k in dd.authnkey}
    assert all(dd_copy.pubkey[k].to_dict() == dd.pubkey[k].to_dict() for k in dd_copy.pubkey)
    assert {k for k in dd_copy.pubkey} == {k for k in dd.pubkey}
    assert all(dd_copy.service[k].to_dict() == dd.service[k].to_dict() for k in dd_copy.service)
    assert {k for k in dd_copy.service} == {k for k in dd.service}
    print('\n\n== 2 == DID Doc de/serialization operates OK:')

    # Exercise accessors
    dd.did = dd_out['id']
    assert dd.did == canon_did(dd_out['id'])
    try:
        dd.set(['neither a service', 'nor a public key'])
        assert False
    except BadDIDDocItem:
        pass
    assert dd.service[[k for k in dd.service][0]].did == dd.did
    print('\n\n== 3 == DID Doc accessors operate OK')

    # One authn key embedded, all possible refs canonical
    dd_in = {
        '@context': 'https://w3id.org/did/v1',
        'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
        'publicKey': [
            {
                'id': '3',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC X...'
            },
            {
                'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL#4',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC 9...'
            }
        ],
        'authentication': [
            {
                'type': 'RsaSignatureAuthentication2018',
                'publicKey': 'did:sov:LjgpST2rjsoxYegQDRm7EL#4'
            },
            {
                'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL#6',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC A...'
            }
        ],
        'service': [
            {
                'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL;0',
                'type': 'Agency',
                'serviceEndpoint': 'https://www.von.ca'
            }
        ]
    }

    dd = DIDDoc.deserialize(dd_in)
    assert len(dd.pubkey) == len(dd_in['publicKey']) + 1
    assert len(dd.authnkey) == len(dd_in['authentication'])

    dd_out = dd.serialize()
    print('\n\n== 4 == DID Doc on mixed reference styles, embedded and ref style authn keys: {}'.format(ppjson(dd_out)))

    # All references canonical where possible; one authn key embedded and one by reference
    dd_in = {
        '@context': 'https://w3id.org/did/v1',
        'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
        'publicKey': [
            {
                'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL#3',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC X...'
            },
            {
                'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL#4',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC 9...'
            }
        ],
        'authentication': [
            {
                'type': 'RsaSignatureAuthentication2018',
                'publicKey': 'did:sov:LjgpST2rjsoxYegQDRm7EL#4'
            },
            {
                'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL#6',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC A...'
            }
        ],
        'service': [
            {
                'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL;0',
                'type': 'DidMessaging',
                'serviceEndpoint': 'https://www.von.ca'
            }
        ]
    }

    dd = DIDDoc.deserialize(dd_in)
    assert len(dd.pubkey) == len(dd_in['publicKey']) + 1
    assert len(dd.authnkey) == len(dd_in['authentication'])

    dd_out = dd.serialize()
    print('\n\n== 5 == DID Doc on canonical refs: {}'.format(ppjson(dd_out)))

    # Minimal as per indy-agent test suite without explicit identifiers
    dd_in = {
        '@context': 'https://w3id.org/did/v1',
        'publicKey': [
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL#keys-1',
                'type': 'Ed25519VerificationKey2018',
                'controller': 'LjgpST2rjsoxYegQDRm7EL',
                'publicKeyBase58': '~XXXXXXXXXXXXXXXX'
            }
        ],
        'service': [
            {
                'type': 'DidMessaging',
                'recipientKeys': ['~XXXXXXXXXXXXXXXX'],
                'serviceEndpoint': 'https://www.von.ca'
            }
        ]
    }

    dd = DIDDoc.deserialize(dd_in)
    assert len(dd.pubkey) == len(dd_in['publicKey'])
    assert len(dd.authnkey) == 0

    dd_out = dd.serialize()
    print('\n\n== 6 == DID Doc miminal style, implcit DID document identifier: {}'.format(
        ppjson(dd_out)))

    # Minimal + ids as per indy-agent test suite with explicit identifiers; novel service recipient key on raw base58
    dd_in = {
        '@context': 'https://w3id.org/did/v1',
        'id': 'LjgpST2rjsoxYegQDRm7EL',
        'publicKey': [
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL#keys-1',
                'type': 'Ed25519VerificationKey2018',
                'controller': 'LjgpST2rjsoxYegQDRm7EL',
                'publicKeyBase58': '~XXXXXXXXXXXXXXXX'
            }
        ],
        'service': [
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL;indy',
                'type': 'DidMessaging',
                'priority': 1,
                'recipientKeys': ['~YYYYYYYYYYYYYYYY'],
                'serviceEndpoint': 'https://www.von.ca'
            }
        ]
    }

    dd = DIDDoc.deserialize(dd_in)
    assert len(dd.pubkey) == 1 + len(dd_in['publicKey'])
    assert len(dd.authnkey) == 0

    dd_out = dd.serialize()
    print('\n\n== 7 == DID Doc miminal style plus explicit idents and novel raw base58 service recip key: {}'.format(
        ppjson(dd_out)))

    # Minimal + ids as per indy-agent test suite with explicit identifiers; novel service recipient key on raw base58
    dd_in = {
        '@context': 'https://w3id.org/did/v1',
        'id': 'LjgpST2rjsoxYegQDRm7EL',
        'publicKey': [
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL#keys-1',
                'type': 'Ed25519VerificationKey2018',
                'controller': 'LjgpST2rjsoxYegQDRm7EL',
                'publicKeyBase58': '~XXXXXXXXXXXXXXXX'
            },
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL#keys-2',
                'type': 'Ed25519VerificationKey2018',
                'controller': 'LjgpST2rjsoxYegQDRm7EL',
                'publicKeyBase58': '~YYYYYYYYYYYYYYYY'
            },
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL#keys-3',
                'type': 'Secp256k1VerificationKey2018',
                'controller': 'LjgpST2rjsoxYegQDRm7EL',
                'publicKeyHex': '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71'
            },
            {
                'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL#keys-4',
                'type': 'RsaVerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyPem': '-----BEGIN PUBLIC A...'
            }
        ],
        'service': [
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL;indy',
                'type': 'DidMessaging',
                'priority': 0,
                'recipientKeys': ['~ZZZZZZZZZZZZZZZZ'],
                'serviceEndpoint': 'did:sov:LjgpST2rjsoxYegQDRm7EL;1'
            },
            {
                'id': '1',
                'type': 'one',
                'priority': 1,
                'recipientKeys': [
                    '~XXXXXXXXXXXXXXXX',
                    'did:sov:LjgpST2rjsoxYegQDRm7EL#keys-1'
                ],
                'routingKeys': [
                    'did:sov:LjgpST2rjsoxYegQDRm7EL#keys-4'
                ],
                'serviceEndpoint': 'LjgpST2rjsoxYegQDRm7EL;2'
            },
            {
                'id': '2',
                'type': 'two',
                'priority': 2,
                'recipientKeys': [
                    '~XXXXXXXXXXXXXXXX',
                    'did:sov:LjgpST2rjsoxYegQDRm7EL#keys-1'
                ],
                'routingKeys': [
                    'did:sov:LjgpST2rjsoxYegQDRm7EL#keys-4'
                ],
                'serviceEndpoint': 'https://www.two.ca/two'
            }
        ]
    }

    dd = DIDDoc.deserialize(dd_in)
    assert len(dd.pubkey) == 1 + len(dd_in['publicKey'])
    assert len(dd.authnkey) == 0
    assert {s.priority for s in dd.service.values()} == {0, 1, 2}
    assert len(dd.service) == 3
    assert all(len(dd.service[k].to_dict()['recipientKeys']) == 1 for k in dd.service)
    assert 'routingKeys' not in dd.service['did:sov:LjgpST2rjsoxYegQDRm7EL;indy'].to_dict()
    assert all(len(dd.service[k].to_dict()['routingKeys']) == 1
        for k in ('did:sov:LjgpST2rjsoxYegQDRm7EL;1', 'did:sov:LjgpST2rjsoxYegQDRm7EL;2'))


    dd_out = dd.serialize()
    print('\n\n== 8 == DID Doc on mixed service routing and recipient keys: {}'.format(ppjson(dd_out)))

    pk = PublicKey(
        dd.did,
        '99',
        '~AAAAAAAAAAAAAAAA',
        PublicKeyType.ED25519_SIG_2018,
        dd.did,
        True)
    dd.set(pk)
    assert len(dd.pubkey) == 2 + len(dd_in['publicKey'])
    assert canon_ref(dd.did, '99', '#') in dd.pubkey
    assert len(dd.authnkey) == 1

    service = Service(
        dd.did,
        'abc',
        'IndyAgent',
        [pk],
        [pk],
        'http://www.abc.ca/123'
    )
    dd.set(service)
    assert len(dd.service) == 4
    assert canon_ref(dd.did, 'abc', ';') in dd.service
    print('\n\n== 9 == DID Doc adds public key and service via set() OK')

    # Exercise missing service recipient key
    dd_in = {
        '@context': 'https://w3id.org/did/v1',
        'id': 'LjgpST2rjsoxYegQDRm7EL',
        'publicKey': [
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL#keys-1',
                'type': 'Ed25519VerificationKey2018',
                'controller': 'LjgpST2rjsoxYegQDRm7EL',
                'publicKeyBase58': '~XXXXXXXXXXXXXXXX'
            }
        ],
        'service': [
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL;indy',
                'type': 'DidMessaging',
                'priority': 1,
                'recipientKeys': [
                    'did:sov:LjgpST2rjsoxYegQDRm7EL#keys-3'
                ],
                'serviceEndpoint': 'https://www.von.ca'
            }
        ]
    }

    try:
        dd = DIDDoc.deserialize(dd_in)
        assert False
    except AbsentDIDDocItem:
        pass
    print('\n\n== 10 == DID Doc on underspecified service key fails as expected')

    # Minimal as per W3C Example 2, draft 0.12
    dd_in = {
        '@context': 'https://w3id.org/did/v1',
        'id': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
        'authentication': [
            {
                'id': 'LjgpST2rjsoxYegQDRm7EL#keys-1',
                'type': 'Ed25519VerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyBase58': '~XXXXXXXXXXXXXXXX'
            }
        ],
        'service': [
            {
                'type': 'DidMessaging',
                'serviceEndpoint': 'https://example.com/endpoint/8377464'
            }
        ]
    }

    dd = DIDDoc.deserialize(dd_in)
    assert len(dd.pubkey) == 1
    assert len(dd.authnkey) == 1
    assert len(dd.service) == 1

    dd_out = dd.serialize()
    print('\n\n== 11 == Minimal DID Doc (no pubkey except authentication) as per W3C spec parses OK: {}'.format(
        ppjson(dd_out)))

    # Exercise no-identifier case
    dd_in = {
        '@context': 'https://w3id.org/did/v1',
        'authentication': [
            {
                'type': 'Ed25519VerificationKey2018',
                'controller': 'did:sov:LjgpST2rjsoxYegQDRm7EL',
                'publicKeyBase58': '~XXXXXXXXXXXXXXXX'
            }
        ],
        'service': [
            {
                'type': 'DidMessaging',
                'serviceEndpoint': 'https://example.com/endpoint/8377464'
            }
        ]
    }

    try:
        dd = DIDDoc.deserialize(dd_in)
        assert False
    except AbsentDIDDocItem:
        pass
    print('\n\n== 12 == DID Doc without identifier rejected as expected')

    # Exercise reference canonicalization, including failure paths
    try:
        canon_ref('not-a-DID', ref=dd.did, delimiter='#')
        assert False
    except BadIdentifier:
        pass

    try:
        canon_ref(dd.did, ref='did:sov:not-a-DID', delimiter='#')
        assert False
    except BadIdentifier:
        pass
    
    urlref = 'https://www.clafouti-quasar.ca:8443/supply-management/fruit/index.html'
    assert canon_ref(dd.did, ref=urlref) == urlref
    print('\n\n== 13 == Reference canonicalization operates as expected')

    assert PublicKeyType.get('no-such-type') is None
    pubkey0 = dd.pubkey[[k for k in dd.pubkey][0]]
    was_authn = pubkey0.authn
    pubkey0.authn = not was_authn
    assert pubkey0.authn != was_authn
    print('\n\n== 14 == Changed authentication setting for DIDDoc {} in public key {}, now {}'.format(
        pubkey0.did,
        pubkey0.id,
        repr(pubkey0)))
Exemple #10
0
    def deserialize(cls, did_doc: dict) -> 'DIDDoc':
        """
        Construct DIDDoc object from dict representation.

        Raise BadIdentifier for bad DID. Raise AbsentDIDDocItem for missing mandatory item.

        :param did_doc: DIDDoc dict reprentation.
        :return: DIDDoc from input json.
        """

        rv = None
        if 'id' in did_doc:
            rv = DIDDoc(did_doc['id'])
        else:  # heuristic: get DID to serve as DID document identifier from first OK-looking public key
            for section in ('publicKey', 'authentication'):
                if rv is None and section in did_doc:
                    for key_spec in did_doc[section]:
                        try:
                            pubkey_did = canon_did(
                                resource(key_spec.get('id', '')))
                            if ok_did(pubkey_did):
                                rv = DIDDoc(pubkey_did)
                                break
                        except BadIdentifier:  # no identifier here, move on to next candidate
                            break
            if rv is None:
                LOGGER.debug(
                    'DIDDoc.deserialize <!< no identifier in DID document')
                raise AbsentDIDDocItem('No identifier in DID document')

        for pubkey in did_doc.get(
                'publicKey', {}
        ):  # include all public keys, authentication pubkeys by reference
            pubkey_type = PublicKeyType.get(pubkey['type'])
            authn = any(
                canon_ref(rv.did, ak) == canon_ref(rv.did, pubkey['id'])
                for ak in did_doc.get('authentication', '')
                if isinstance(ak, str))
            key = PublicKey(  # initialization canonicalizes id
                rv.did, pubkey['id'], pubkey[pubkey_type.specifier],
                pubkey_type, canon_did(pubkey['controller']), authn)
            rv.pubkey[key.id] = key

        for akey in did_doc.get('authentication',
                                {}):  # include embedded authentication keys
            if isinstance(akey, collections.Mapping):
                pubkey_type = PublicKeyType.get(akey['type'])
                key = PublicKey(  # initialization canonicalized id
                    rv.did, akey['id'], akey[pubkey_type.specifier],
                    pubkey_type, canon_did(akey['controller']), True)
                rv.pubkey[key.id] = key

        for service in did_doc.get('service', {}):
            endpoint = service['serviceEndpoint']
            svc = Service(  # initialization canonicalizes id
                rv.did,
                service.get(
                    'id',
                    canon_ref(rv.did,
                              'assigned-service-{}'.format(len(rv.service)),
                              ';')), service['type'],
                rv.add_service_pubkeys(service, 'recipientKeys'),
                rv.add_service_pubkeys(service,
                                       ['mediatorKeys', 'routingKeys']),
                canon_ref(rv.did, endpoint, ';') if ';' in endpoint else
                endpoint, service.get('priority', None))
            rv.service[svc.id] = svc

        return rv