async def handle(self, content_type, data): try: if content_type == DIDExchange.MESSAGE_CONTENT_TYPE: kwargs = json.loads(data) msg = Message(**kwargs) elif content_type in WIRED_CONTENT_TYPES: msg = await DIDExchange.unpack_agent_message(data, self.get_wallet()) else: raise RuntimeError('Unknown content_type "%s"' % content_type) await self.__log('Receive', msg.to_dict()) if msg.type == DIDExchange.INVITE: await self.__receive_invitation(msg) elif msg.type == DIDExchange.RESPONSE: await self.__receive_connection_response(msg) elif msg.type == DIDExchange.PROBLEM_REPORT: if self.status == DIDExchangeStatus.Requested: # Stay in same state - retryable pass else: raise ImpossibleStatus() else: logging.error('Unexpected message type: %s' % msg.type) except Exception as e: if not isinstance(e, MachineIsDone): logging.exception('Base machine terminated with exception') await self.done()
async def handle(cls, agent_name: str, wire_message: bytes, my_label: str = None, my_endpoint: str = None) -> bool: unpacked = await WalletAgent.unpack_message(agent_name, wire_message) kwargs = json.loads(unpacked['message']) message = Message(**kwargs) print('***** feature 0037 handle message *****') if 'sender_verkey' in unpacked: sender_verkey = unpacked['sender_verkey'] else: sender_verkey = None if 'recipient_verkey' in unpacked: recipient_verkey = unpacked['recipient_verkey'] else: recipient_verkey = None print('sender_verkey: ' + sender_verkey) print('recipient_verkey: ' + recipient_verkey) print(json.dumps(message.to_dict(), indent=2, sort_keys=True)) print('***************************************') if message.get('@type', None) is None: return False if not cls.endorsement(message): return False state_machine_id = cls.get_state_machine_id(unpacked['sender_verkey']) if message.type in [ PresentProofProtocol.REQUEST_PRESENTATION, AckMessage.ACK ]: if message.type == PresentProofProtocol.REQUEST_PRESENTATION: machine_class = PresentProofProtocol.ProverStateMachine await WalletAgent.start_state_machine( status=PresentProofStatus.Null, ttl=PresentProofProtocol.STATE_MACHINE_TTL, agent_name=agent_name, machine_class=machine_class, machine_id=state_machine_id) await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.WIRED_CONTENT_TYPE, data=wire_message) return True elif message.type in [PresentProofProtocol.PRESENTATION]: await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.WIRED_CONTENT_TYPE, data=wire_message) return True elif message.type == PresentProofProtocol.PROBLEM_REPORT: await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.WIRED_CONTENT_TYPE, data=wire_message) return True else: return False
def validate(msg: Message): msg.check_for_attrs( [ ('@type', RoutingMessage.FORWARD), 'to', 'msg', ] )
def validate(msg: Message): msg.check_for_attrs( [ ('@type', AckMessage.ACK), 'status', '~thread', ] )
def validate_pre_sig(response: Message): response.check_for_attrs( [ ('@type', DIDExchange.RESPONSE), '~thread', 'connection~sig' ] )
def validate(msg: Message): msg.check_for_attrs([ ('@type', BasicMessage.MESSAGE), '~l10n', 'sent_time', 'content', ]) Message.check_for_attrs_in_message([('locale', 'en')], msg['~l10n'])
async def pack(msg: Message, wallet: WalletConnection, their_ver_key, routing_keys: list, my_ver_key=None) -> bytes: if not routing_keys: raise RuntimeError('routing_keys must not be empty') payload = await wallet.pack_message( Serializer.serialize(msg).decode(RoutingMessage.ENC), their_ver_key, my_ver_key ) keys_map = {} for n in range(len(routing_keys)-1, 0, -1): # example: IF routing_keys = ['k1', 'k2', 'k3'] THEN n = [2,1] outer_key = routing_keys[n] inner_key = routing_keys[n-1] keys_map[outer_key] = inner_key keys_map[routing_keys[0]] = their_ver_key for outer_key in routing_keys: inner_key = keys_map[outer_key] forwarded = Message({ '@type': RoutingMessage.FORWARD, 'to': inner_key, 'msg': json.loads(payload.decode(RoutingMessage.ENC)) }) payload = await wallet.pack_message( Serializer.serialize(forwarded).decode(RoutingMessage.ENC), outer_key, ) return payload
def build(comment: str = None, response_requested: bool = True): creation = {'@type': TrustPing.PING, '@id': str(uuid.uuid4())} if comment: creation['comment'] = comment if response_requested in [True, False]: creation['response_requested'] = response_requested return Message(creation)
def build(req_id: str, my_did: str, my_vk: str, endpoint: str) -> Message: return Message({ '@type': DIDExchange.RESPONSE, '@id': str(uuid.uuid4()), '~thread': {Message.THREAD_ID: req_id, Message.SENDER_ORDER: 0}, 'connection': { 'did': my_did, 'did_doc': { "@context": "https://w3id.org/did/v1", "id": my_did, "publicKey": [{ "id": my_did + "#keys-1", "type": "Ed25519VerificationKey2018", "controller": my_did, "publicKeyBase58": my_vk }], "service": [{ "id": my_did + ";indy", "type": "IndyAgent", "recipientKeys": [my_vk], # "routingKeys": ["<example-agency-verkey>"], "serviceEndpoint": endpoint, }], } } })
async def handle(cls, agent_name: str, wire_message: bytes, my_label: str = None, my_endpoint: str = None) -> bool: unpacked = await WalletAgent.unpack_message(agent_name, wire_message) kwargs = json.loads(unpacked['message']) message = Message(**kwargs) if message.get('@type', None) is None: return False if not cls.endorsement(message): return False for protocol_version in ["1.1", "1.0"]: type_issue_credential = cls.set_protocol_version( cls.ISSUE_CREDENTIAL, protocol_version) type_offer_credential = cls.set_protocol_version( cls.OFFER_CREDENTIAL, protocol_version) type_request_credential = cls.set_protocol_version( cls.REQUEST_CREDENTIAL, protocol_version) state_machine_id = cls.get_state_machine_id( unpacked['sender_verkey']) if message.type in [type_issue_credential, type_offer_credential]: machine_class = IssueCredentialProtocol.HolderSateMachine if message.type == type_offer_credential: await WalletAgent.start_state_machine( status=IssueCredentialStatus.Null, ttl=IssueCredentialProtocol.STATE_MACHINE_TTL, agent_name=agent_name, machine_class=machine_class, machine_id=state_machine_id, protocol_version=protocol_version) await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.WIRED_CONTENT_TYPE, data=wire_message) return True elif message.type in [type_request_credential, AckMessage.ACK]: await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.WIRED_CONTENT_TYPE, data=wire_message) return True return False
def build(ping_id: str): return Message({ '@type': TrustPing.PING_RESPONSE, '~thread': { Message.THREAD_ID: ping_id, Message.SENDER_ORDER: 0 } })
async def handle(cls, agent_name: str, wire_message: bytes, my_label: str=None, my_endpoint: str=None) -> bool: unpacked = await WalletAgent.unpack_message(agent_name, wire_message) kwargs = json.loads(unpacked['message']) message = Message(**kwargs) if message.get('@type', None) is None: return False if message.type == cls.REQUEST: state_machine_id = unpacked['sender_verkey'] machine_class = DIDExchange.DIDExchangeInviterStateMachine await WalletAgent.start_state_machine( agent_name=agent_name, machine_class=machine_class, machine_id=state_machine_id, endpoint=my_endpoint, label=my_label, status=DIDExchangeStatus.Invited ) await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.WIRED_CONTENT_TYPE, data=wire_message ) return True elif message.type == DIDExchange.RESPONSE: state_machine_id = message['connection~sig']['signer'] await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.WIRED_CONTENT_TYPE, data=wire_message ) return True elif message.type in [TrustPing.PING, TrustPing.PING_RESPONSE]: state_machine_id = unpacked['sender_verkey'] await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.WIRED_CONTENT_TYPE, data=wire_message ) return True elif message.type == DIDExchange.PROBLEM_REPORT: state_machine_id = message.to_dict().get('connection~sig', {}).get('signer') if state_machine_id: await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.WIRED_CONTENT_TYPE, data=wire_message ) return True else: logging.error('Problem report', message.as_json()) return True else: return False
def build(thread_id: str, status: str="OK", sender_order: int=0) -> Message: return Message({ '@type': AckMessage.ACK, 'status': status, '~thread': { Message.THREAD_ID: thread_id, Message.SENDER_ORDER: sender_order } })
async def receive_invite_message(cls, msg: Message, agent_name: str, pass_phrase: str, my_label: str, my_endpoint: str, ttl: int) -> str: """ Receive and save invite. This interaction represents an out-of-band communication channel. In the future and in practice, these sort of invitations will be received over any number of channels such as SMS, Email, QR Code, NFC, etc. In this iteration, invite messages are received from the admin interface as a URL after being copied and pasted from another agent instance. The URL is formatted as follows: https://<domain>/<path>?c_i=<invitationstring> The invitation string is a base64 url encoded json string. Structure of an invite message: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation", "label": "Alice", "did": "did:sov:QmWbsNYhMrjHiqZDTUTEJs" } Or, in the case of a peer DID: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation", "label": "Alice", "key": "8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K", "endpoint": "https://example.com/endpoint" } Currently, only peer DID format is supported. """ if not cls.endorsement(msg): return None connection_key = msg['recipientKeys'][0] state_machine_id = connection_key log_channel_name = 'invite-log/' + uuid.uuid4().hex await WalletAgent.start_state_machine( agent_name=agent_name, machine_class=DIDExchange.DIDExchangeInviteeStateMachine, machine_id=state_machine_id, ttl=ttl, endpoint=my_endpoint, label=my_label, status=DIDExchangeStatus.Null, log_channel_name=log_channel_name ) await WalletAgent.invoke_state_machine( agent_name=agent_name, id_=state_machine_id, content_type=cls.MESSAGE_CONTENT_TYPE, data=msg.as_json() ) return log_channel_name
async def unpack(forwarded: Message, wallet: WalletConnection) -> (Message, str, str): RoutingMessage.validate(forwarded) forwarded_wired = json.dumps(forwarded['msg']).encode(RoutingMessage.ENC) unpacked = await wallet.unpack_message(forwarded_wired) kwargs = json.loads(unpacked['message']) forwarder = Message(**kwargs) sender_verkey = unpacked.get('sender_verkey', None) recipient_verkey = unpacked.get('recipient_verkey', None) return forwarder, recipient_verkey, sender_verkey
def build_problem_report_for_connections(cls, problem_code, problem_str, thread_id: str=None) -> Message: initialized = { "@type": "{}/problem_report".format(cls.FAMILY), "problem-code": problem_code, "explain": problem_str } if thread_id: initialized['~thread'] = {Message.THREAD_ID: thread_id, Message.SENDER_ORDER: 0} return Message(initialized)
def validate(request): request.check_for_attrs( [ ('@type', DIDExchange.REQUEST), '@id', 'label', DIDExchange.CONNECTION ] ) Message.check_for_attrs_in_message( [ DIDDoc.DID, DIDDoc.DID_DOC ], request[DIDExchange.CONNECTION] ) DIDDoc.validate(request[DIDExchange.CONNECTION][DIDDoc.DID_DOC])
async def validate_common_message_blocks(cls, msg: Message, problem_code: str, context: Context): try: msg.validate_common_blocks() return True, None except MessageValidationException as e: logging.exception('Validation error while parsing message: %s' % msg.as_json()) if context.their_did: err_msg = cls.build_problem_report_for_connections( problem_code, str(e.exception), thread_id=msg.id) return False, err_msg else: return False, None except Exception as e: logging.exception('Validation error while parsing message: %s' % str(e)) return False, None
def build(content: str) -> Message: sent_time = datetime.datetime.utcnow().replace( tzinfo=datetime.timezone.utc).isoformat(' ') return Message({ '@type': BasicMessage.MESSAGE, '~l10n': { 'locale': 'en' }, 'sent_time': sent_time, 'content': content })
async def test_forwarding_message(): wallet1_name = 'test_wallet_1' wallet2_name = 'test_wallet_2' pass_phrase = 'pass_phrase' await remove_wallets(wallet1_name, wallet2_name) conn_sender = WalletConnection(wallet1_name, pass_phrase) conn_recipient = WalletConnection(wallet2_name, pass_phrase) await conn_sender.create() await conn_recipient.create() try: await conn_sender.open() await conn_recipient.open() # build expected message message = BasicMessage.build(content='Test content') # generate keys sender_verkey = await conn_sender.create_key() recipient_verkey = await conn_recipient.create_key() routing_key1 = await conn_recipient.create_key() routing_key2 = await conn_recipient.create_key() routing_keys = [routing_key1, routing_key2] print('-------- values ----------') print('sender_verkey: ' + sender_verkey) print('recipient_verkey: ' + recipient_verkey) print('routing_keys: ' + str(routing_keys)) print('--------------------------') # emulate communication wired = await RoutingMessage.pack(message, conn_sender, recipient_verkey, routing_keys, sender_verkey) unpacked = await conn_recipient.unpack_message(wired) kwargs = json.loads(unpacked['message']) message = Message(**kwargs) assert message.type == RoutingMessage.FORWARD assert unpacked.get('recipient_verkey') == routing_key2 assert unpacked.get('sender_verkey') is None assert message.data.get('to') == routing_key1 message, recipient_vk, sender_vk = await RoutingMessage.unpack( message, conn_recipient) assert message.type == RoutingMessage.FORWARD assert recipient_vk == routing_key1 assert sender_vk is None assert message.data.get('to') == recipient_verkey message, recipient_vk, sender_vk = await RoutingMessage.unpack( message, conn_recipient) assert message.type == BasicMessage.MESSAGE assert recipient_vk == recipient_verkey assert sender_vk == sender_verkey assert message.data.get('content') == 'Test content' finally: await conn_sender.delete() await conn_recipient.delete()
def propose_credential( cls, comment: str = None, locale: str = DEF_LOCALE, proposal_attrib: List[ProposedAttrib] = None, schema_id: str = None, schema_name: str = None, schema_version: str = None, schema_issuer_did: str = None, cred_def_id: str = None, issuer_did: str = None, proposal_attrib_translation: List[AttribTranslation] = None): data = { '@type': cls.PROPOSE_CREDENTIAL, '~l10n': { "locale": locale }, } if comment: data['comment'] = comment if schema_id: data['schema_id'] = schema_id if schema_name: data['schema_name'] = schema_name if schema_version: data['schema_version'] = schema_version if schema_issuer_did: data['schema_issuer_did'] = schema_issuer_did if cred_def_id: data['cred_def_id'] = cred_def_id if issuer_did: data['issuer_did'] = issuer_did if proposal_attrib: data['credential_proposal'] = { "@type": cls.CREDENTIAL_PREVIEW_TYPE, "attributes": [attrib.to_json() for attrib in proposal_attrib] } if proposal_attrib_translation: data['~attach'] = [{ "@type": cls.CREDENTIAL_TRANSLATION_TYPE, "id": cls.CREDENTIAL_TRANSLATION_ID, '~l10n': { "locale": locale }, "mime-type": "application/json", "data": { "json": [ trans.to_json() for trans in proposal_attrib_translation ] } }] return Message(data)
def validate(response: Message, req_id: str): response.check_for_attrs( [ ('@type', DIDExchange.RESPONSE), '~thread', 'connection' ] ) Message.check_for_attrs_in_message( [ (Message.THREAD_ID, req_id) ], response['~thread'] ) Message.check_for_attrs_in_message( [ DIDDoc.DID, DIDDoc.DID_DOC ], response[DIDExchange.CONNECTION] ) DIDDoc.validate(response[DIDExchange.CONNECTION][DIDDoc.DID_DOC])
def extract_verkey_endpoint(msg: Message, key: str) -> (Optional, Optional): """ Extract verkey and endpoint that will be used to send message back to the sender of this message. Might return None. """ did_doc = BasicMessage.extract_did_doc(msg, key) vks = did_doc.get('publicKey') vk = vks[0].get('publicKeyBase58') if vks and isinstance( vks, list) and len(vks) > 0 else None endpoints = msg.get(key, {}).get(DIDDoc.DID_DOC, {}).get('service') endpoint = endpoints[0].get( 'serviceEndpoint') if endpoints and isinstance( endpoints, list) and len(endpoints) > 0 else None return vk, endpoint
def build(label: str, connection_key: str, endpoint: str) -> str: msg = Message({ '@type': DIDExchange.INVITE, 'label': label, 'recipientKeys': [connection_key], 'serviceEndpoint': endpoint, # routing_keys not specified, but here is where they would be put in the invite. }) b64_invite = base64.urlsafe_b64encode( bytes( Serializer.serialize(msg).decode('utf-8'), 'ascii' ) ).decode('ascii') return '{}?c_i={}'.format(endpoint, b64_invite)
def validate(did_doc): Message.check_for_attrs_in_message( ['@context', 'publicKey', 'service'], did_doc) for publicKeyBlock in did_doc['publicKey']: Message.check_for_attrs_in_message( ['id', 'type', 'controller', 'publicKeyBase58'], publicKeyBlock) for serviceBlock in did_doc['service']: Message.check_for_attrs_in_message( [('type', 'IndyAgent'), 'recipientKeys', 'serviceEndpoint'], serviceBlock)
async def generate_invite_message(cls, label: str, endpoint: str, agent_name: str, pass_phrase: str, extra: dict=None) -> Message: """ Generate new connection invitation. This interaction represents an out-of-band communication channel. In the future and in practice, these sort of invitations will be received over any number of channels such as SMS, Email, QR Code, NFC, etc. Structure of an invite message: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation", "label": "Alice", "did": "did:sov:QmWbsNYhMrjHiqZDTUTEJs" } Or, in the case of a peer DID: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation", "label": "Alice", "did": "did:peer:oiSqsNYhMrjHiqZDTUthsw", "key": "8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K", "endpoint": "https://example.com/endpoint" } Currently, only peer DID is supported. """ await WalletAgent.ensure_agent_is_open(agent_name, pass_phrase) connection_key = await WalletAgent.create_key(agent_name, pass_phrase) # Store connection key # await WalletAgent.add_wallet_record(agent_name, pass_phrase, 'connection_key', connection_key, connection_key) data = { '@type': cls.INVITE, 'label': label, 'recipientKeys': [connection_key], 'serviceEndpoint': endpoint, # routingKeys not specified, but here is where they would be put in the invite. } if extra: data.update(extra) invite_msg = Message(data) return invite_msg
async def __receive_connection_ack(self, msg: Message): if self.status == DIDExchangeStatus.Responded: try: TrustPing.Ping.validate(msg) if msg.get('response_requested'): pong = TrustPing.Pong.build(msg.id) to_did = msg.context['to_did'] await DIDExchange.send_message_to_agent(to_did, pong, self.get_wallet()) except: err_msg = DIDExchange.build_problem_report_for_connections( DIDExchange.RESPONSE_FOR_UNKNOWN_REQUEST, 'Uncknown ack thread id', thread_id=msg.id ) to_did = msg.context['to_did'] await DIDExchange.send_message_to_agent(to_did, err_msg, self.get_wallet()) else: await self.done() else: raise ImpossibleStatus()
async def __validate_cred_offer(self, msg: Message, context: Context): offer_attaches = msg.to_dict().get('offers~attach', None) if isinstance(offer_attaches, dict): offer_attaches = [offer_attaches] if (not type(offer_attaches) is list) or ( type(offer_attaches) is list and len(offer_attaches) == 0): await self.__send_problem_report( problem_code=IssueCredentialProtocol. OFFER_PROCESSING_ERROR, problem_str= 'Expected offer~attach must contains credOffer and credDef', context=context, thread_id=msg.id) await self.done() offer = offer_attaches[0] offer_body = None cred_def_body = None for attach in offer_attaches: raw_base64 = attach.get('data', {}).get('base64', None) if raw_base64: payload = json.loads(base64.b64decode(raw_base64).decode()) offer_fields = [ 'key_correctness_proof', 'nonce', 'schema_id', 'cred_def_id' ] cred_def_fields = [ 'value', 'type', 'ver', 'schemaId', 'id', 'tag' ] if all([field in payload.keys() for field in offer_fields ]): # check if cred offer content offer_body = { attr: val for attr, val in payload.items() if attr in offer_fields } if all( [field in payload.keys() for field in cred_def_fields ]): # check if cred def content cred_def_body = { attr: val for attr, val in payload.items() if attr in cred_def_fields } if not offer_body: await self.__send_problem_report( problem_code=IssueCredentialProtocol. OFFER_PROCESSING_ERROR, problem_str= 'Expected offer~attach must contains Payload with offer', context=context, thread_id=msg.id) await self.done() if cred_def_body: cred_def_id = cred_def_body['id'] await indy_sdk_utils.store_cred_def(self.get_wallet(), cred_def_id, cred_def_body) else: await self.__send_problem_report( problem_code=IssueCredentialProtocol. OFFER_PROCESSING_ERROR, problem_str= 'Expected offer~attach must contains Payload with cred_def data', context=context, thread_id=msg.id) await self.done() attaches = msg.to_dict().get('~attach', None) if attaches: if isinstance(attaches, dict): attaches = [attaches] for attach in attaches: if attach.get( '@type', None ) == IssueCredentialProtocol.ISSUER_SCHEMA_TYPE: issuer_schema_body = attach['data']['json'] issuer_schema_id = issuer_schema_body['id'] await indy_sdk_utils.store_issuer_schema( self.get_wallet(), issuer_schema_id, issuer_schema_body) return offer, offer_body, cred_def_body
async def handle(self, content_type, data): try: if content_type in WIRED_CONTENT_TYPES: msg, context = await PresentProofProtocol.unpack_agent_message( data, self.get_wallet()) self.to = context.their_did success, err_msg = await PresentProofProtocol.validate_common_message_blocks( msg, PresentProofProtocol.REQUEST_NOT_ACCEPTED, context) if not success and err_msg: await PresentProofProtocol.send_message_to_agent( context.their_did, err_msg, self.get_wallet()) else: raise RuntimeError('Unsupported content_type "%s"' % content_type) if msg.type == PresentProofProtocol.REQUEST_PRESENTATION: if self.status == PresentProofStatus.Null: await self.__log('Received request presentation', msg.to_dict()) request_attach = msg['request_presentations~attach'] if isinstance(request_attach, list): request_attach = request_attach[0] payload = json.loads( base64.b64decode( request_attach['data']['base64']).decode()) proof_request = payload search_handle = await self.get_wallet( ).prover_search_credentials_for_proof_req( proof_request=proof_request) try: requested_attributes = proof_request.get( 'requested_attributes', {}) requested_predicates = proof_request.get( 'requested_predicates', {}) schemas_json = dict() cred_defs_json = dict() prover_requested_creds = { 'self_attested_attributes': {}, 'requested_attributes': {}, 'requested_predicates': {} } for attr_referent in requested_attributes.keys(): cred_for_attr = await self.get_wallet( ).prover_fetch_credentials_for_proof_req( search_handle=search_handle, item_referent=attr_referent, count=1) if cred_for_attr: cred_info = cred_for_attr[0]['cred_info'] schema_id = cred_info['schema_id'] schemas_json[ schema_id] = await indy_sdk_utils.get_issuer_schema( self.get_wallet(), schema_id) cred_def_id = cred_info['cred_def_id'] cred_defs_json[ cred_def_id] = await indy_sdk_utils.get_cred_def( self.get_wallet(), cred_def_id) prover_requested_creds[ 'requested_attributes'][ attr_referent] = { 'cred_id': cred_info['referent'], 'revealed': True } for pred_referent in requested_predicates.keys(): cred_for_predicate = await self.get_wallet( ).prover_fetch_credentials_for_proof_req( search_handle=search_handle, item_referent=pred_referent, count=1) if cred_for_predicate: cred_info = cred_for_predicate[0][ 'cred_info'] schema_id = cred_info['schema_id'] schemas_json[ schema_id] = await indy_sdk_utils.get_issuer_schema( self.get_wallet(), schema_id) cred_def_id = cred_info['cred_def_id'] cred_defs_json[ cred_def_id] = await indy_sdk_utils.get_cred_def( self.get_wallet(), cred_def_id) prover_requested_creds[ 'requested_predicates'][ pred_referent] = { 'cred_id': cred_info['referent'], } finally: await self.get_wallet( ).prover_close_credentials_search_for_proof_req( search_handle) master_secret_name = settings.INDY['WALLET_SETTINGS'][ 'PROVER_MASTER_SECRET_NAME'] proof = await self.get_wallet().prover_create_proof( proof_req=proof_request, requested_creds=prover_requested_creds, link_secret_id=master_secret_name, schemas=schemas_json, cred_defs=cred_defs_json, rev_states=None) self.ack_message_id = uuid.uuid4().hex data = { "@type": PresentProofProtocol.PRESENTATION, "@id": self.ack_message_id, "presentations~attach": [{ "@id": "libindy-presentation-" + self.ack_message_id, "mime-type": "application/json", "data": { "base64": base64.b64encode( json.dumps(proof).encode()).decode() } }] } message_proof = Message(data) await PresentProofProtocol.send_message_to_agent( self.to, message_proof, self.get_wallet()) self.status = PresentProofStatus.PresentationSent await self.__log(event='Send Proof', details=data) else: await self.__send_problem_report( problem_code=PresentProofProtocol. REQUEST_PROCESSING_ERROR, problem_str='Impossible state machine state', context=context, thread_id=msg.id) raise ImpossibleStatus elif msg.type == AckMessage.ACK: await self.__log('Received ack', msg.to_dict()) if self.status == PresentProofStatus.PresentationSent: await self.done() else: await self.__send_problem_report( problem_code=PresentProofProtocol. RESPONSE_NOT_ACCEPTED, problem_str='UnExpected message type for status', context=context, thread_id=msg.id) raise ImpossibleStatus elif msg.type == PresentProofProtocol.PROBLEM_REPORT: await self.__log('Received problem report', msg.to_dict()) await self.done() else: await self.__send_problem_report( problem_code=PresentProofProtocol. RESPONSE_FOR_UNKNOWN_REQUEST, problem_str='Unknown message type', context=context, thread_id=msg.id) except Exception as e: if not isinstance(e, MachineIsDone): logging.exception('Base machine terminated with exception') await self.done()
async def handle(self, content_type, data): try: if content_type == PresentProofProtocol.MESSAGE_CONTENT_TYPE: command = str(data.get('command', None)) if command == PresentProofProtocol.CMD_START: await self.__log('Start verifying', data) # Store Context comment = data.get('comment', None) locale = data.get( 'locale', None) or PresentProofProtocol.DEF_LOCALE proof_request = data['proof_request'] self.proof_request_buffer = json.dumps(proof_request) translation = data.get('translation', None) translation = [ AttribTranslation(**item) for item in translation ] if translation else None id_suffix = uuid.uuid4().hex data = { "@type": PresentProofProtocol.REQUEST_PRESENTATION, "comment": "some comment", "request_presentations~attach": [{ "@id": "libindy-request-presentation-" + id_suffix, "mime-type": "application/json", "data": { "base64": base64.b64encode( json.dumps( proof_request).encode()).decode() } }] } if comment: data['comment'] = comment data['~l10n'] = {"locale": locale} self.comment = comment self.locale = locale if translation: data['~attach'] = [{ "@type": PresentProofProtocol. CREDENTIAL_TRANSLATION_TYPE, "id": PresentProofProtocol.CREDENTIAL_TRANSLATION_ID, '~l10n': { "locale": locale }, "mime-type": "application/json", "data": { "json": [trans.to_json() for trans in translation] } }] if self.expires_time: data['~timing'] = { "expires_time": self.expires_time } message_request = Message(data) await PresentProofProtocol.send_message_to_agent( self.to, message_request, self.get_wallet()) self.status = PresentProofStatus.RequestSent await self.__log(event='Send Request-Presentation', details=data) elif command == PresentProofProtocol.CMD_STOP: if self.to and self.status != PresentProofStatus.Null: err_msg = PresentProofProtocol.build_problem_report_for_connections( problem_code=PresentProofProtocol. REQUEST_NOT_ACCEPTED, problem_str='Actor unexpected stopped issuing', ) await PresentProofProtocol.send_message_to_agent( self.to, err_msg, self.get_wallet()) await self.__log('Actor unexpected stopped issuing') await self.done() else: raise RuntimeError('Unknown command: %s' % command) elif content_type in WIRED_CONTENT_TYPES: msg, context = await PresentProofProtocol.unpack_agent_message( data, self.get_wallet()) success, err_msg = await PresentProofProtocol.validate_common_message_blocks( msg, PresentProofProtocol.REQUEST_NOT_ACCEPTED, context) if not success and err_msg: await PresentProofProtocol.send_message_to_agent( context.their_did, err_msg, self.get_wallet()) if msg.type == PresentProofProtocol.PRESENTATION: await self.__log('Received presentation', msg.to_dict()) if self.status == PresentProofStatus.RequestSent: presentation_attach = msg['presentations~attach'] if isinstance(presentation_attach, list): presentation_attach = presentation_attach[0] payload = json.loads( base64.b64decode(presentation_attach['data'] ['base64']).decode()) proof = payload proof_request = json.loads( self.proof_request_buffer) await self.__log('verifier proof-request', proof_request) await self.__log(core.const.PROOF, proof) schemas = dict() cred_defs = dict() for ident in proof['identifiers']: schema_id = ident['schema_id'] cred_def_id = ident['cred_def_id'] schema = await indy_sdk_utils.get_issuer_schema( self.get_wallet(), schema_id ) or await get_issuer_schema(schema_id) if schema: schemas[schema_id] = schema else: _, schema = await core.ledger.get_schema( context.my_did, schema_id) schemas[schema_id] = schema cred_def = await indy_sdk_utils.get_cred_def( self.get_wallet(), cred_def_id ) or await get_cred_def_meta(cred_def_id) if cred_def: cred_defs[ cred_def_id] = self.__prepare_cred_def( cred_def) else: _, cred_def = await core.ledger.get_cred_def( context.my_did, cred_def_id) await indy_sdk_utils.store_cred_def( self.get_wallet(), cred_def_id, cred_def) cred_defs[ cred_def_id] = self.__prepare_cred_def( cred_def) await self.__log('schemas', schemas) await self.__log('cred_defs', cred_defs) success, error_message = await verifier_verify_proof( proof_request=proof_request, proof=proof, schemas=schemas, credential_defs=cred_defs, rev_reg_defs=None, rev_regs=None) await self.__log( 'verify result', dict(success=success, error_message=error_message)) if success: ack = AckMessage.build(msg.id) await PresentProofProtocol.send_message_to_agent( self.to, ack, self.get_wallet()) await self.__log(event='Send Ack', details=ack.to_dict()) await self.__log( event=core.const.VERIFY_SUCCESS) else: if error_message: if 'Invalid structure' in error_message: error_message = None await self.__send_problem_report( problem_code=PresentProofProtocol. VERIFY_ERROR, problem_str=error_message or 'Proof verification finished with errors', context=context, thread_id=msg.id) await self.__log(core.const.VERIFY_ERROR) await self.done() else: await self.__send_problem_report( problem_code=PresentProofProtocol. RESPONSE_NOT_ACCEPTED, problem_str='Impossible state', context=context, thread_id=msg.id) raise ImpossibleStatus() elif msg.type == PresentProofProtocol.PROBLEM_REPORT: await self.__log('Received problem report', msg.to_dict()) await self.done() else: await self.__send_problem_report( problem_code=PresentProofProtocol. RESPONSE_FOR_UNKNOWN_REQUEST, problem_str='Unknown message type', context=context, thread_id=msg.id) except Exception as e: if not isinstance(e, MachineIsDone): print('------------- FEATURE 0037 --------------') print('Exception') print(str(e)) print('----------------------------------------') logging.exception( 'Base machine terminated with exception: %s' % str(e)) await self.done()