Skip to content

Commit

Permalink
feat: support creating request according to invite hs proto
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Bluhm <dbluhm@pm.me>
  • Loading branch information
dbluhm committed Mar 22, 2024
1 parent f74d4e3 commit ab08c6d
Showing 1 changed file with 109 additions and 55 deletions.
164 changes: 109 additions & 55 deletions aries_cloudagent/protocols/didexchange/v1_0/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import json
import logging
from typing import Optional, Sequence, Union
from typing import List, Optional, Sequence, Union

from did_peer_4 import LONG_PATTERN, long_to_short

Expand All @@ -24,12 +24,13 @@
from ....wallet.error import WalletError
from ....wallet.key_type import ED25519
from ...coordinate_mediation.v1_0.manager import MediationManager
from ...coordinate_mediation.v1_0.models.mediation_record import MediationRecord
from ...discovery.v2_0.manager import V20DiscoveryMgr
from ...out_of_band.v1_0.messages.invitation import (
InvitationMessage as OOBInvitationMessage,
)
from ...out_of_band.v1_0.messages.service import Service as OOBService
from .message_types import ARIES_PROTOCOL as DIDEX_1_1
from .message_types import ARIES_PROTOCOL as DIDEX_1_1, DIDEX_1_0
from .messages.complete import DIDXComplete
from .messages.problem_report import DIDXProblemReport, ProblemReportReason
from .messages.request import DIDXRequest
Expand Down Expand Up @@ -109,7 +110,9 @@ async def receive_invitation(
)
else ConnRecord.ACCEPT_MANUAL
)
protocol = protocol or "didexchange/1.0"
protocol = protocol or DIDEX_1_0
if protocol not in ConnRecord.SUPPORTED_PROTOCOLS:
raise DIDXManagerError(f"Unexpected protocol: {protocol}")

service_item = invitation.services[0]
# Create connection record
Expand Down Expand Up @@ -251,7 +254,6 @@ async def create_request_implicit(
mediation_id=mediation_id,
goal_code=goal_code,
goal=goal,
use_public_did=bool(my_public_info),
)
conn_rec.request_id = request._id
conn_rec.state = ConnRecord.State.REQUEST.rfc160
Expand All @@ -271,7 +273,6 @@ async def create_request(
mediation_id: Optional[str] = None,
goal_code: Optional[str] = None,
goal: Optional[str] = None,
use_public_did: bool = False,
) -> DIDXRequest:
"""Create a new connection request for a previously-received invitation.
Expand All @@ -283,8 +284,6 @@ async def create_request(
service endpoint
goal_code: Optional self-attested code for sharing intent of connection
goal: Optional self-attested string for sharing intent of connection
use_public_did: Flag whether to use public DID and omit DID Doc
attachment on request
Returns:
A new `DIDXRequest` message to send to the other agent
Expand All @@ -297,9 +296,6 @@ async def create_request(
or_default=True,
)

my_info = None

# Create connection request message
if my_endpoint:
my_endpoints = [my_endpoint]
else:
Expand All @@ -309,6 +305,55 @@ async def create_request(
my_endpoints.append(default_endpoint)
my_endpoints.extend(self.profile.settings.get("additional_endpoints", []))

if not my_label:
my_label = self.profile.settings.get("default_label")
assert my_label

did_url = None
if conn_rec.their_public_did is not None:
services = await self.resolve_didcomm_services(conn_rec.their_public_did)
if services:
did_url = services[0].id

pthid = conn_rec.invitation_msg_id or did_url

if conn_rec.connection_protocol == DIDEX_1_0:
request = await self._legacy_request_with_attached_doc(
conn_rec, my_label, my_endpoints, mediation_records, goal_code, goal
)
else:
request = await self._qualified_did_request_with_fallback(
conn_rec, my_label, my_endpoints, mediation_records, goal_code, goal
)

request.assign_thread_id(thid=request._id, pthid=pthid)

# Update connection state
conn_rec.request_id = request._id
conn_rec.state = ConnRecord.State.REQUEST.rfc160
async with self.profile.session() as session:
await conn_rec.save(session, reason="Created connection request")

# Idempotent; if routing has already been set up, no action taken
await self._route_manager.route_connection_as_invitee(
self.profile, conn_rec, mediation_records
)

return request

async def _qualified_did_request_with_fallback(
self,
conn_rec: ConnRecord,
my_label: str,
my_endpoints: Sequence[str],
mediation_records: List[MediationRecord],
goal_code: Optional[str],
goal: Optional[str],
) -> DIDXRequest:
"""Create DID Exchange request using a qualified DID.
Fall back to unqualified DID if settings don't cause did:peer emission.
"""
emit_did_peer_4 = self.profile.settings.get("emit_did_peer_4")
emit_did_peer_2 = self.profile.settings.get("emit_did_peer_2")
if emit_did_peer_2 and emit_did_peer_4:
Expand All @@ -317,18 +362,60 @@ async def create_request(
using did:peer:4"
)

if conn_rec.my_did:
if conn_rec.my_did: # DID should be public or qualified
async with self.profile.session() as session:
wallet = session.inject(BaseWallet)
my_info = await wallet.get_local_did(conn_rec.my_did)

posture = DIDPosture.get(my_info.metadata)
if posture not in (
DIDPosture.PUBLIC,
DIDPosture.POSTED,
) and not my_info.did.startswith("did:"):
# The DID has been previously set and is not public or qualified...
# Must fallback
return await self._legacy_request_with_attached_doc(
conn_rec, my_label, my_endpoints, mediation_records, goal_code, goal
)
elif emit_did_peer_4:
my_info = await self.create_did_peer_4(my_endpoints, mediation_records)
conn_rec.my_did = my_info.did
elif emit_did_peer_2:
my_info = await self.create_did_peer_2(my_endpoints, mediation_records)
conn_rec.my_did = my_info.did
else:
# Create new DID for connection
return await self._legacy_request_with_attached_doc(
conn_rec, my_label, my_endpoints, mediation_records, goal_code, goal
)

did = conn_rec.my_did
assert did, "DID must be set on connection record"
if not did.startswith("did:"):
did = f"did:sov:{did}"

request = DIDXRequest(
label=my_label,
did=did,
goal_code=goal_code,
goal=goal,
)
return request

async def _legacy_request_with_attached_doc(
self,
conn_rec: ConnRecord,
my_label: str,
my_endpoints: Sequence[str],
mediation_records: List[MediationRecord],
goal_code: Optional[str],
goal: Optional[str],
) -> DIDXRequest:
"""Create a DID Exchange request using an unqualified DID."""
if conn_rec.my_did:
async with self.profile.session() as session:
wallet = session.inject(BaseWallet)
my_info = await wallet.get_local_did(conn_rec.my_did)
else:
async with self.profile.session() as session:
wallet = session.inject(BaseWallet)
my_info = await wallet.create_local_did(
Expand All @@ -337,56 +424,23 @@ async def create_request(
)
conn_rec.my_did = my_info.did

if use_public_did or emit_did_peer_2 or emit_did_peer_4:
# Omit DID Doc attachment if we're using a public DID
did_doc = None
attach = None
did = conn_rec.my_did
if not did.startswith("did:"):
did = f"did:sov:{did}"
else:
did_doc = await self.create_did_document(
my_info,
my_endpoints,
mediation_records=mediation_records,
)
attach = AttachDecorator.data_base64(did_doc.serialize())
async with self.profile.session() as session:
wallet = session.inject(BaseWallet)
await attach.data.sign(my_info.verkey, wallet)
did = conn_rec.my_did

did_url = None
if conn_rec.their_public_did is not None:
services = await self.resolve_didcomm_services(conn_rec.their_public_did)
if services:
did_url = services[0].id

pthid = conn_rec.invitation_msg_id or did_url

if not my_label:
my_label = self.profile.settings.get("default_label")
did_doc = await self.create_did_document(
my_info,
my_endpoints,
mediation_records=mediation_records,
)
attach = AttachDecorator.data_base64(did_doc.serialize())
async with self.profile.session() as session:
wallet = session.inject(BaseWallet)
await attach.data.sign(my_info.verkey, wallet)

request = DIDXRequest(
label=my_label,
did=did,
did=my_info.did,
did_doc_attach=attach,
goal_code=goal_code,
goal=goal,
)
request.assign_thread_id(thid=request._id, pthid=pthid)

# Update connection state
conn_rec.request_id = request._id
conn_rec.state = ConnRecord.State.REQUEST.rfc160
async with self.profile.session() as session:
await conn_rec.save(session, reason="Created connection request")

# Idempotent; if routing has already been set up, no action taken
await self._route_manager.route_connection_as_invitee(
self.profile, conn_rec, mediation_records
)

return request

async def receive_request(
Expand Down

0 comments on commit ab08c6d

Please sign in to comment.