Пример #1
0
    async def create_cred_req(self, cred_offer_json: str,
                              cd_id: str) -> (str, str):
        """
        Create credential request as HolderProver and store in wallet; return credential json and metadata json.

        Raise AbsentLinkSecret if link secret not set.

        :param cred_offer_json: credential offer json
        :param cd_id: credential definition identifier
        :return: cred request json and corresponding metadata json as created and stored in wallet
        """

        LOGGER.debug(
            'HolderProver.create_cred_req >>> cred_offer_json: %s, cd_id: %s',
            cred_offer_json, cd_id)

        self._assert_link_secret('create_cred_req')

        # Check that ledger has schema on ledger where cred def expects - in case of pool reset with extant wallet
        cred_def_json = await self.get_cred_def(cd_id)
        schema_seq_no = int(json.loads(cred_def_json)['schemaId'])
        schema_json = await self.get_schema(schema_seq_no)
        schema = json.loads(schema_json)
        if not schema:
            LOGGER.debug(
                'HolderProver.create_cred_req: <!< absent schema@#%s, cred req may be for another ledger',
                schema_seq_no)
            raise AbsentSchema(
                'Absent schema@#{}, cred req may be for another ledger'.format(
                    schema_seq_no))
        (cred_req_json, cred_req_metadata_json
         ) = await anoncreds.prover_create_credential_req(
             self.wallet.handle, self.did, cred_offer_json, cred_def_json,
             self._link_secret)
        rv = (cred_req_json, cred_req_metadata_json)

        LOGGER.debug('HolderProver.create_cred_req <<< %s', rv)
        return rv
Пример #2
0
    async def get_schema(self, index: Union[SchemaKey, int, str]) -> str:
        """
        Get schema from ledger by SchemaKey namedtuple (origin DID, name, version),
        sequence number, or schema identifier.

        Raise AbsentSchema for no such schema, logging any error condition and raising
        BadLedgerTxn on bad request.

        Retrieve the schema from the agent's schema cache if it has it; cache it
        en passant if it does not (and there is a corresponding schema on the ledger).

        :param index: schema key (origin DID, name, version), sequence number, or schema identifier
        :return: schema json, parsed from ledger
        """

        LOGGER.debug('_BaseAgent.get_schema >>> index: %s', index)

        rv_json = json.dumps({})
        with SCHEMA_CACHE.lock:
            if SCHEMA_CACHE.contains(index):
                LOGGER.info(
                    '_BaseAgent.get_schema: got schema %s from schema cache',
                    index)
                rv_json = SCHEMA_CACHE[index]
                LOGGER.debug('_BaseAgent.get_schema <<< %s', rv_json)
                return json.dumps(rv_json)

            if isinstance(index, SchemaKey) or (isinstance(index, str)
                                                and ':2:' in index):
                s_id = schema_id(
                    *index) if isinstance(index, SchemaKey) else index
                s_key = schema_key(s_id)
                req_json = await ledger.build_get_schema_request(
                    self.did, s_id)
                resp_json = await self._submit(req_json)
                resp = json.loads(resp_json)

                if not ('result' in resp and resp['result'].get(
                        'data', {}).get('attr_names', None)):
                    LOGGER.debug(
                        '_BaseAgent.get_schema: <!< no schema exists on %s',
                        index)
                    raise AbsentSchema('No schema exists on {}'.format(index))
                try:
                    (_, rv_json
                     ) = await ledger.parse_get_schema_response(resp_json)
                except IndyError:  # ledger replied, but there is no such schema
                    LOGGER.debug(
                        '_BaseAgent.get_schema: <!< no schema exists on %s',
                        index)
                    raise AbsentSchema('No schema exists on {}'.format(index))
                SCHEMA_CACHE[s_key] = json.loads(
                    rv_json
                )  # cache indexes by both txn# and schema key en passant
                LOGGER.info('_BaseAgent.get_schema: got schema %s from ledger',
                            index)

            elif isinstance(
                    index, (int, str)
            ):  # ':2:' not in index - it's a stringified int txn num if it's a str
                txn_json = await self.get_txn(int(index))
                txn = json.loads(txn_json)
                if txn.get(
                        'type', None
                ) == '101':  # {} for no such txn; 101 marks indy-sdk schema txn type
                    rv_json = await self.get_schema(
                        SchemaKey(txn['metadata']['from'],
                                  txn['data']['data']['name'],
                                  txn['data']['data']['version']))
                else:
                    LOGGER.info(
                        '_BaseAgent.get_schema: no schema at seq #%s on ledger',
                        index)

            else:
                LOGGER.debug(
                    '_BaseAgent.get_schema: <!< bad schema index type')
                raise AbsentSchema(
                    'Attempt to get schema on ({}) {} , must use schema key or an int'
                    .format(type(index), index))

        LOGGER.debug('_BaseAgent.get_schema <<< %s', rv_json)
        return rv_json
Пример #3
0
    async def verify_proof(self, proof_req: dict, proof: dict) -> str:
        """
        Verify proof as Verifier. Raise AbsentRevReg if a proof cites a revocation registry
        that does not exist on the distributed ledger.

        :param proof_req: proof request as Verifier creates, as per proof_req_json above
        :param proof: proof as HolderProver creates
        :return: json encoded True if proof is valid; False if not
        """

        LOGGER.debug('Verifier.verify_proof >>> proof_req: %s, proof: %s',
                     proof_req, proof)

        s_id2schema = {}
        cd_id2cred_def = {}
        rr_id2rr_def = {}
        rr_id2rr = {}
        proof_ids = proof['identifiers']

        for proof_id in proof_ids:
            # schema
            s_id = proof_id['schema_id']
            if s_id not in s_id2schema:
                schema = json.loads(
                    await self.get_schema(s_id))  # add to cache en passant
                if not schema:
                    LOGGER.debug(
                        'Verifier.verify_proof: <!< absent schema %s, proof req may be for another ledger',
                        s_id)
                    raise AbsentSchema(
                        'Absent schema {}, proof req may be for another ledger'
                        .format(s_id))
                s_id2schema[s_id] = schema

            # cred def
            cd_id = proof_id['cred_def_id']
            if cd_id not in cd_id2cred_def:
                cred_def = json.loads(
                    await self.get_cred_def(cd_id))  # add to cache en passant
                cd_id2cred_def[cd_id] = cred_def

            # rev reg def
            rr_id = proof_id['rev_reg_id']
            if not rr_id:
                continue

            rr_def_json = await self._get_rev_reg_def(rr_id)
            rr_id2rr_def[rr_id] = json.loads(rr_def_json)

            # timestamp
            timestamp = proof_id['timestamp']
            with REVO_CACHE.lock:
                revo_cache_entry = REVO_CACHE.get(rr_id, None)
                (rr_json, _) = await revo_cache_entry.get_state_json(
                    self._build_rr_state_json, timestamp, timestamp)
                if rr_id not in rr_id2rr:
                    rr_id2rr[rr_id] = {}
                rr_id2rr[rr_id][timestamp] = json.loads(rr_json)

        rv = json.dumps(await anoncreds.verifier_verify_proof(
            json.dumps(proof_req), json.dumps(proof), json.dumps(s_id2schema),
            json.dumps(cd_id2cred_def), json.dumps(rr_id2rr_def),
            json.dumps(rr_id2rr)))

        LOGGER.debug('Verifier.verify_proof <<< %s', rv)
        return rv
Пример #4
0
    async def create_proof(self, proof_req: dict, creds: dict,
                           requested_creds: dict) -> str:
        """
        Create proof as HolderProver.

        Raise:
            * AbsentLinkSecret if link secret not set
            * CredentialFocus on attempt to create proof on no creds or multiple creds for a credential definition
            * AbsentTails if missing required tails file
            * BadRevStateTime if a timestamp for a revocation registry state in the proof request
              occurs before revocation registry creation
            * IndyError for any other indy-sdk error.
            * AbsentInterval if creds missing non-revocation interval, but cred def supports revocation

        :param proof_req: proof request as per get_creds() above
        :param creds: credentials to prove
        :param requested_creds: data structure with self-attested attribute info, requested attribute info
            and requested predicate info, assembled from get_creds() and filtered for content of interest. I.e.,

        ::

            {
                'self_attested_attributes': {},
                'requested_attributes': {
                    'attr0_uuid': {
                        'cred_id': string,
                        'timestamp': integer,  # for revocation state
                        'revealed': bool
                    },
                    ...
                },
                'requested_predicates': {
                    'predicate0_uuid': {
                        'cred_id': string,
                        'timestamp': integer  # for revocation state
                    }
                }
            }

        :return: proof json
        """

        LOGGER.debug(
            'HolderProver.create_proof >>> proof_req: %s, creds: %s, requested_creds: %s',
            proof_req, creds, requested_creds)

        self._assert_link_secret('create_proof')

        x_uuids = [
            attr_uuid for attr_uuid in creds['attrs']
            if len(creds['attrs'][attr_uuid]) != 1
        ]
        if x_uuids:
            LOGGER.debug(
                'HolderProver.create_proof: <!< creds specification out of focus (non-uniqueness)'
            )
            raise CredentialFocus(
                'Proof request requires unique cred per attribute; violators: {}'
                .format(x_uuids))

        s_id2schema = {}  # schema identifier to schema
        cd_id2cred_def = {
        }  # credential definition identifier to credential definition
        rr_id2timestamp = {
        }  # revocation registry of interest to timestamp of interest (or None)
        rr_id2cr_id = {
        }  # revocation registry of interest to credential revocation identifier
        for referents in {**creds['attrs'], **creds['predicates']}.values():
            interval = referents[0].get('interval', None)
            cred_info = referents[0]['cred_info']
            s_id = cred_info['schema_id']
            if s_id not in s_id2schema:
                schema = json.loads(
                    await self.get_schema(s_id))  # add to cache en passant
                if not schema:
                    LOGGER.debug(
                        'HolderProver.create_proof: <!< absent schema %s, proof req may be for another ledger',
                        s_id)
                    raise AbsentSchema(
                        'Absent schema {}, proof req may be for another ledger'
                        .format(s_id))
                s_id2schema[s_id] = schema

            cd_id = cred_info['cred_def_id']
            if cd_id not in cd_id2cred_def:
                cred_def = json.loads(
                    await self.get_cred_def(cd_id))  # add to cache en passant
                cd_id2cred_def[cd_id] = cred_def

            rr_id = cred_info['rev_reg_id']
            if rr_id:
                await self._sync_revoc(
                    rr_id)  # link tails file to its rr_id if it's new
                if interval:
                    if rr_id not in rr_id2timestamp:
                        if interval['to'] > int(time()):
                            LOGGER.debug(
                                'HolderProver.create_proof: <!< interval to %s for rev reg %s is in the future',
                                interval['to'], rr_id)
                            raise BadRevStateTime(
                                'Revocation registry {} timestamp {} is in the future'
                                .format(rr_id, interval['to']))
                        rr_id2timestamp[rr_id] = interval['to']
                elif 'revocation' in cd_id2cred_def[cd_id]['value']:
                    LOGGER.debug(
                        'HolderProver.create_proof: <!< creds on cred def id %s missing non-revocation interval',
                        cd_id)
                    raise AbsentInterval(
                        'Creds on cred def id {} missing non-revocation interval'
                        .format(cd_id))
                if rr_id in rr_id2cr_id:
                    continue
                rr_id2cr_id[rr_id] = cred_info['cred_rev_id']

        rr_id2rev_state = {}  # revocation registry identifier to its state
        with REVO_CACHE.lock:
            for rr_id in rr_id2timestamp:
                revo_cache_entry = REVO_CACHE.get(rr_id, None)
                tails = revo_cache_entry.tails if revo_cache_entry else None
                if tails is None:  # missing tails file
                    LOGGER.debug(
                        'HolderProver.create_proof: <!< missing tails file for rev reg id %s',
                        rr_id)
                    raise AbsentTails(
                        'Missing tails file for rev reg id {}'.format(rr_id))
                rr_def_json = await self._get_rev_reg_def(rr_id)
                (rr_delta_json,
                 ledger_timestamp) = await revo_cache_entry.get_delta_json(
                     self._build_rr_delta_json, rr_id2timestamp[rr_id],
                     rr_id2timestamp[rr_id])
                rr_state_json = await anoncreds.create_revocation_state(
                    tails.reader_handle, rr_def_json, rr_delta_json,
                    ledger_timestamp, rr_id2cr_id[rr_id])
                rr_id2rev_state[rr_id] = {
                    rr_id2timestamp[rr_id]: json.loads(rr_state_json)
                }

        rv = await anoncreds.prover_create_proof(self.wallet.handle,
                                                 json.dumps(proof_req),
                                                 json.dumps(requested_creds),
                                                 self._link_secret,
                                                 json.dumps(s_id2schema),
                                                 json.dumps(cd_id2cred_def),
                                                 json.dumps(rr_id2rev_state))
        LOGGER.debug('HolderProver.create_proof <<< %s', rv)
        return rv