def check_encoding(proof_req: dict, proof: dict) -> bool:
        """
        Return whether the proof's raw values correspond to their encodings
        as cross-referenced against proof request.

        :param proof request: proof request
        :param proof: corresponding proof to check
        :return: True if OK, False for encoding mismatch
        """

        LOGGER.debug('Verifier.check_encoding <<< proof_req: %s, proof: %s',
                     proof_req, proof)

        cd_id2proof_id = {}  # invert proof['identifiers'] per cd_id
        p_preds = {}  # cd_id and attr to bound
        for idx in range(len(proof['identifiers'])):
            cd_id = proof['identifiers'][idx]['cred_def_id']
            cd_id2proof_id[cd_id] = idx  # since at most 1 cred per cred def
            p_preds[cd_id] = {
                ge_proof['predicate']['attr_name']:
                ge_proof['predicate']['value']
                for ge_proof in proof['proof']['proofs'][idx]['primary_proof']
                ['ge_proofs']
            }

        for (uuid, req_attr) in proof_req['requested_attributes'].items(
        ):  # proof req xref proof per revealed attr
            for attr in [req_attr['name']
                         ] if 'name' in req_attr else req_attr['names']:
                canon_attr = canon(attr)
                proof_ident_idx = cd_id2proof_id[req_attr['restrictions'][0]
                                                 ['cred_def_id']]
                primary_enco = proof['proof']['proofs'][proof_ident_idx][
                    'primary_proof']['eq_proof']['revealed_attrs'].get(
                        canon_attr)
                if primary_enco is None:
                    continue  # requested but declined from revelation in proof: must appear in a predicate
                req_revealed = proof['requested_proof'].get(
                    'revealed_attr_groups', {}).get(
                        uuid, {'values': {
                            attr: None
                        }})['values'][attr] or proof['requested_proof'][
                            'revealed_attrs'][uuid]
                if primary_enco != req_revealed['encoded']:
                    LOGGER.debug('Verifier.check_proof_encoding <<< False')
                    return False
                if primary_enco != encode(req_revealed['raw']):
                    LOGGER.debug('Verifier.check_proof_encoding <<< False')
                    return False

        for (uuid, req_pred) in proof_req['requested_predicates'].items(
        ):  # proof req xref proof per pred
            canon_attr = canon(req_pred['name'])
            if p_preds[req_pred['restrictions'][0]['cred_def_id']].get(
                    canon_attr) != req_pred['p_value']:
                LOGGER.debug('Verifier.check_proof_encoding <<< False')
                return False

        LOGGER.debug('Verifier.check_proof_encoding <<< True')
        return True
Beispiel #2
0
async def test_canon():
    print(Ink.YELLOW('\n\n== Testing Attribute Canonicalization =='))
    assert canon('testAttr') == 'testattr'
    assert canon(' test Attr ') == 'testattr'
    assert canon('testattr') == 'testattr'
    assert canon('testAttrZeroOneTwoThree') == 'testattrzeroonetwothree'
    print('\n\n== Canonicalization for attr values works as expected')
Beispiel #3
0
def revealed_attrs(proof: dict) -> dict:
    """
    Fetch revealed attributes from input proof and return dict mapping credential definition identifiers
    to dicts, each dict mapping attribute names to (raw) values, for processing in further creds downstream.

    :param proof: indy-sdk proof as dict
    :return: dict mapping cred-ids to dicts, each mapping revealed attribute names to (raw) values
    """

    rv = {}
    sub_index2cd_id = {}

    for (sub_index, sub_proof) in enumerate(proof['proof']['proofs']):
        cd_id = proof['identifiers'][sub_index]['cred_def_id']
        sub_index2cd_id[sub_index] = cd_id
        rv[cd_id] = sub_proof['primary_proof']['eq_proof']['revealed_attrs']  # start with encoded

    for revealed_attr in proof['requested_proof'].get('revealed_attrs', {}).values():
        cd_id = sub_index2cd_id[revealed_attr['sub_proof_index']]
        for (canon_attr, enco) in rv[cd_id]:
            if revealed_attr['encoded'] == enco:
                rv[cd_id][canon_attr] = revealed_attr['raw']  # replace encoded (replaces repeated values one at a time)
                break

    for revealed_attr_group in proof['requested_proof'].get('revealed_attr_groups', {}).values():
        cd_id = sub_index2cd_id[revealed_attr_group['sub_proof_index']]
        for (attr, spec) in revealed_attr_group['values'].items():
            rv[cd_id][canon(attr)] = spec['raw']

    return rv
Beispiel #4
0
    async def build_proof_req_json(self, cd_id2spec: dict) -> str:
        """
        Build and return indy-sdk proof request for input attributes and non-revocation intervals by cred def id.

        :param cd_id2spec: dict mapping cred def ids to:

            - (optionally) 'attrs': lists of names of attributes of interest (omit for all, empty list or None for none)
            - (optionally) '>=': (pred) inclusive int lower-bounds of interest (omit, empty list, or None for none)
            - (optionally) '>': (pred) exclusive int lower-bounds of interest (omit, empty list, or None for none)
            - (optionally) '<=': (pred) inclusive int upper-bounds of interest (omit, empty list, or None for none)
            - (optionally) '<': (pred) exclusive int upper-bounds of interest (omit, empty list, or None for none)
            - (optionally), 'interval': either
                - (2-tuple) pair of epoch second counts marking 'from' and 'to' timestamps, or
                - | single epoch second count to set 'from' and 'to' the same; default
                  | (now, now) for cred defs supporting revocation or None otherwise; e.g.,

        ::

            {
                'Vx4E82R17q...:3:CL:16:tag': {
                    'attrs': [  # request attrs 'name' and 'favouriteDrink' from this cred def's schema
                        'name',
                        'favouriteDrink'
                    ],
                    '>=': {  # request predicate score>=80 from this cred def
                        'score': 80
                    }
                    '<=': {  # request ranking <=10 from this cred def
                        'ranking': 10
                    }
                    'interval': 1528116008  # same instant for all attrs and preds of corresponding schema
                },
                'R17v42T4pk...:3:CL:19:tag': None,  # request all attrs, no preds, default intervals on all attrs
                'e3vc5K168n...:3:CL:23:tag': {},  # request all attrs, no preds, default intervals on all attrs
                'Z9ccax812j...:3:CL:27:tag': {  # request all attrs, no preds, this interval on all attrs
                    'interval': (1528112408, 1528116008)
                },
                '9cHbp54C8n...:3:CL:37:tag': {  # request no attrs and some predicates; specify interval
                    'attrs': [],  # or equivalently, 'attrs': None
                    '>=': {
                        'employees': '50'  # nicety: implementation converts to int for caller
                    },
                    '>=': {
                        'revenue': '10000000'  # nicety: implementation converts to int for caller
                        'ebidta': 0
                    }
                    'interval': (1528029608, 1528116008)
                },
                '6caBcmLi33...:3:CL:41:tag': {  # all attrs, one pred, default intervals to now on attrs & pred
                    '>': {
                        'regEpoch': 1514782800
                    }
                },
                ...
            }

        :return: indy-sdk proof request json
        """

        LOGGER.debug('Verifier.build_proof_req_json >>> cd_id2spec: %s',
                     cd_id2spec)

        cd_id2schema = {}
        now = int(time())
        rv = {
            'nonce': str(int(time())),
            'name': 'proof_req',
            'version': '0.0',
            'requested_attributes': {},
            'requested_predicates': {}
        }

        for cd_id in cd_id2spec:
            if not ok_cred_def_id(cd_id):
                LOGGER.debug(
                    'Verifier.build_proof_req_json <!< Bad cred def id %s',
                    cd_id)
                raise BadIdentifier('Bad cred def id {}'.format(cd_id))

            interval = None
            cred_def = json.loads(await self.get_cred_def(cd_id))
            seq_no = cred_def_id2seq_no(cd_id)
            cd_id2schema[cd_id] = json.loads(await self.get_schema(seq_no))

            if 'revocation' in cred_def['value']:
                fro_to = cd_id2spec[cd_id].get(
                    'interval',
                    (now, now)) if cd_id2spec[cd_id] else (now, now)
                interval = {
                    'from': fro_to if isinstance(fro_to, int) else min(fro_to),
                    'to': fro_to if isinstance(fro_to, int) else max(fro_to)
                }

            for attr in (cd_id2spec[cd_id].get(
                    'attrs', cd_id2schema[cd_id]['attrNames']) or []
                         if cd_id2spec[cd_id] else
                         cd_id2schema[cd_id]['attrNames']):
                attr_uuid = '{}_{}_uuid'.format(seq_no, canon(attr))
                rv['requested_attributes'][attr_uuid] = {
                    'name': attr,
                    'restrictions': [{
                        'cred_def_id': cd_id
                    }]
                }
                if interval:
                    rv['requested_attributes'][attr_uuid][
                        'non_revoked'] = interval

            for pred in Predicate:
                for attr in (cd_id2spec[cd_id].get(pred.value.math, {}) or {}
                             if cd_id2spec[cd_id] else {}):
                    pred_uuid = '{}_{}_{}_uuid'.format(seq_no, canon(attr),
                                                       pred.value.fortran)
                    try:
                        rv['requested_predicates'][pred_uuid] = {
                            'name':
                            attr,
                            'p_type':
                            pred.value.math,
                            'p_value':
                            Predicate.to_int(
                                cd_id2spec[cd_id][pred.value.math][attr]),
                            'restrictions': [{
                                'cred_def_id': cd_id
                            }]
                        }
                    except ValueError:
                        LOGGER.info(
                            'cannot build %s predicate on non-int bound %s for %s',
                            pred.value.fortran,
                            cd_id2spec[cd_id][pred.value.math][attr], attr)
                        continue  # int conversion failed - reject candidate
                    if interval:
                        rv['requested_predicates'][pred_uuid][
                            'non_revoked'] = interval

        LOGGER.debug('Verifier.build_proof_req_json <<< %s', json.dumps(rv))
        return json.dumps(rv)
Beispiel #5
0
async def test_canon_cred_wql():

    print(Ink.YELLOW('\n\n== Testing credential WQL canonicalization =='))

    invariant = [
        {},
        {
            'attr::test::marker': '1'
        },
        {
            'schema_id': None
        },
        {
            '$or': [{
                'attr::test::value': '0'
            }, {
                'attr::test::value': {
                    '$gt': '10'
                }
            }, {
                'attr::test::value': {
                    '$lt': '-10'
                }
            }]
        },
        {  # and
            'attr::test::marker': '1',
            'attr::test::value': {
                '$in': ['1', '2', '3', '5', '8', '13']
            },
            'attr::another::value': {
                '$like': 'hello%'
            }
        }
    ]

    assert all(canon_cred_wql(q) == q for q in invariant)
    print(
        '\n\n== Canonicalization for invariant credential WQL works as expected'
    )

    # simplest case
    q = {'attr::testAttributeName::marker': 1}
    canon_q = canon_cred_wql(q)
    assert all(canon_q[canon(k)] == raw(q[k]) for k in q)

    # and
    q = {
        'attr::testAttributeName::marker': 1,
        'attr::testAttributeName::value': 0
    }
    canon_q = canon_cred_wql(q)
    assert all(canon_q[canon(k)] == raw(q[k]) for k in q)

    # or
    q = {
        '$or': [{
            'attr::testAttributeName::value': 0
        }, {
            'attr::testAttributeName::value': 1
        }, {
            'attr::testAttributeName::value': 2
        }]
    }
    canon_q = canon_cred_wql(q)
    assert canon_q['$or'] == [
        {
            'attr::testattributename::value': '0'
        },
        {
            'attr::testattributename::value': '1'
        },
        {
            'attr::testattributename::value': '2'
        }  # canonicalize tag names
    ]

    # and, not, like
    q = {
        'attr::testAttributeName::value': {
            '$like': '%'
        },
        '$not': {
            '$or': [
                {
                    'attr::testAttributeName::value': 0
                },
                {
                    'attr::testAttributeName::value': 1
                },
                {
                    'attr::testAttributeName::value': {
                        '$gt': 10
                    }
                },
                {
                    'attr::testAttributeName::value': {
                        '$in': [-3, -7]
                    }
                },
            ]
        }
    }
    canon_q = canon_cred_wql(q)
    assert canon_q['attr::testattributename::value'] == {'$like': '%'}
    canon_q.pop('attr::testattributename::value')
    assert canon_q['$not']['$or'] == [
        {
            'attr::testattributename::value': '0'
        },
        {
            'attr::testattributename::value': '1'
        },
        {
            'attr::testattributename::value': {
                '$gt': '10'
            }
        },
        {
            'attr::testattributename::value': {
                '$in': ['-3', '-7']
            }
        },
    ]
    canon_q.pop('$not')
    assert not canon_q

    # bad 'or'
    q = {
        '$or': {
            'attr::testAttributeName::value': 0,
            'attr::testAttributeName::value': 1
        }
    }
    try:
        canon_q = canon_cred_wql(q)
        assert False
    except BadWalletQuery:
        pass

    print('\n\n== Canonicalization for credential WQL works as expected')