Ejemplo n.º 1
0
    async def _create_rev_reg(self, rr_id: str, rr_size: int = None) -> None:
        """
        Create revocation registry and new tails file (and association to
        corresponding revocation registry definition via symbolic link) for input
        revocation registry identifier.

        :param rr_id: revocation registry identifier
        :param rr_size: revocation registry size (defaults to 256)
        """

        LOGGER.debug('Issuer._create_rev_reg >>> rr_id: %s, rr_size: %s',
                     rr_id, rr_size)

        if not ok_rev_reg_id(rr_id):
            LOGGER.debug('Issuer._create_rev_reg <!< Bad rev reg id %s', rr_id)
            raise BadIdentifier('Bad rev reg id {}'.format(rr_id))

        rr_size = rr_size or 256
        (cd_id, tag) = rev_reg_id2cred_def_id_tag(rr_id)

        LOGGER.info(
            'Creating revocation registry (capacity %s) for rev reg id %s',
            rr_size, rr_id)
        tails_writer_handle = await blob_storage.open_writer(
            'default',
            json.dumps({
                'base_dir': Tails.dir(self._dir_tails, rr_id),
                'uri_pattern': ''
            }))
        apriori = Tails.unlinked(self._dir_tails)
        (rr_id, rrd_json,
         rre_json) = await anoncreds.issuer_create_and_store_revoc_reg(
             self.wallet.handle, self.did, 'CL_ACCUM', tag, cd_id,
             json.dumps({
                 'max_cred_num': rr_size,
                 'issuance_type': 'ISSUANCE_ON_DEMAND'
             }), tails_writer_handle)
        delta = Tails.unlinked(self._dir_tails) - apriori
        if len(delta) != 1:
            LOGGER.debug(
                'Issuer._create_rev_reg: <!< Could not create tails file for rev reg id: %s',
                rr_id)
            raise CorruptTails(
                'Could not create tails file for rev reg id {}'.format(rr_id))
        tails_hash = basename(delta.pop())
        Tails.associate(self._dir_tails, rr_id, tails_hash)

        with REVO_CACHE.lock:
            rrd_req_json = await ledger.build_revoc_reg_def_request(
                self.did, rrd_json)
            await self._sign_submit(rrd_req_json)
            await self._get_rev_reg_def(rr_id)  # add to cache en passant

        rre_req_json = await ledger.build_revoc_reg_entry_request(
            self.did, rr_id, 'CL_ACCUM', rre_json)
        await self._sign_submit(rre_req_json)

        LOGGER.debug('Issuer._create_rev_reg <<<')
Ejemplo n.º 2
0
async def post_tails(request, rr_id):
    """
    Post tails file to server. Multipart file name must be tails hash.

    :param request: Sanic request structure
    :param rr_id: rev reg id for revocation registry to which tails file pertains
    :return: empty text string
    """

    if not ok_rev_reg_id(rr_id):
        LOGGER.error('POST cited bad rev reg id %s', rr_id)
        raise InvalidUsage('POST cited bad rev reg id {}'.format(rr_id))

    # curl uses 'data', python requests uses 'file', there may be others
    req_key = set(k for k in request.files
        if request.files[k]
        and isinstance(request.files[k], list)
        and isinstance(request.files[k][0], SanicReqFile)).pop()
    tails_hash = request.files[req_key][0].name
    if not Tails.ok_hash(tails_hash):
        LOGGER.error('POST attached file named with bad tails file hash %s', tails_hash)
        raise InvalidUsage('POST attached file named with bad tails file hash {}'.format(tails_hash))

    dir_tails = pjoin(dirname(dirname(abspath(__file__))), 'tails')
    dir_cd_id = Tails.dir(dir_tails, rr_id)
    makedirs(dir_cd_id, exist_ok=True)

    if Tails.linked(dir_tails, rr_id):
        LOGGER.error('POST attached tails file %s, already present', rr_id)
        raise Forbidden('POST attached tails file {}, already present'.format(rr_id))

    path_tails_hash = pjoin(dir_cd_id, tails_hash)
    if exists(path_tails_hash):
        LOGGER.error('POST attached tails file %s, already present at %s', rr_id, path_tails_hash)
        raise Forbidden('POST attached tails file {}, already present at {}'.format(rr_id, path_tails_hash))

    with open(path_tails_hash, 'wb') as fh_tails:
        fh_tails.write(request.files[req_key][0].body)

    Tails.associate(dir_tails, rr_id, tails_hash)
    LOGGER.info('Associated link %s to POST tails file attachment saved to %s', rr_id, path_tails_hash)

    return response.text('')
Ejemplo n.º 3
0
async def sync_prover(dir_tails: str, host: str, port: int,
                      remote_only: set) -> None:
    """
    Synchronize for prover: download any tails files appearing remotely but not locally.

    :param dir_tails: local tails directory
    :param host: tails server host
    :param port: tails server port
    :param remote_only: paths to remote rev reg ids without corresponding local tails files
    """

    if not remote_only:
        return

    for rr_id in remote_only:
        dir_cd_id = Tails.dir(dir_tails, rr_id)
        makedirs(dir_cd_id, exist_ok=True)
        url = 'http://{}:{}/tails/{}'.format(host, port, rr_id)
        try:
            resp = requests.get(url, stream=True)
            if resp.status_code == requests.codes.ok:
                re_tails_hash = re.search('filename="(.+)"',
                                          resp.headers['content-disposition'])
                tails_hash = re_tails_hash.group(
                    1) if re_tails_hash.lastindex > 0 else None

                if tails_hash:
                    logging.info('Downloaded: url %s tails-hash %s', url,
                                 tails_hash)
                    with open(join(dir_cd_id, tails_hash), 'wb') as fh_tails:
                        for chunk in resp.iter_content(chunk_size=1024):
                            if chunk:
                                fh_tails.write(chunk)
                    Tails.associate(dir_tails, rr_id, tails_hash)
                else:
                    logging.error(
                        'Download: url %s, responded with no tails-hash', url)

            else:
                logging.error('Download: url %s, responded with status %s',
                              url, resp.status_code)
        except RequestsConnectionError:
            logging.error('GET connection refused: %s', url)
Ejemplo n.º 4
0
async def post_tails(request: Request, rr_id: str, epoch: int) -> HTTPResponse:
    """
    Post tails file to server, auth-encrypted from issuer (by DID) to tails server anchor.
    Multipart file name must be tails hash.

    :param request: Sanic request structure
    :param rr_id: revocation registry identifier
    :param epoch: current EPOCH time, must be within configured proximity to current server time
    :return: empty text response
    """

    if not ok_rev_reg_id(rr_id):
        LOGGER.error('POST cited bad rev reg id %s', rr_id)
        return response.text('POST cited bad rev reg id {}'.format(rr_id), status=400)
    did = rr_id.split(':')[0]

    if not await is_current(int(epoch)):
        LOGGER.error('POST epoch %s in too far from current server time', epoch)
        return response.text('POST epoch {} is too far from current server time'.format(epoch), status=400)

    tails_hash = request.files['tails-file'][0].name
    if not Tails.ok_hash(tails_hash):
        LOGGER.error('POST attached file named with bad tails file hash %s', tails_hash)
        return response.text('POST attached file named with bad tails file hash {}'.format(tails_hash), status=400)

    dir_tails = join(dirname(dirname(realpath(__file__))), 'tails')
    dir_cd_id = Tails.dir(dir_tails, rr_id)

    makedirs(dir_cd_id, exist_ok=True)

    if Tails.linked(dir_tails, rr_id):
        LOGGER.error('POST attached tails file %s, already present', rr_id)
        return response.text('POST attached tails file {}, already present'.format(rr_id), status=403)

    path_tails_hash = join(dir_cd_id, tails_hash)
    if exists(path_tails_hash):
        LOGGER.error('POST attached tails file %s, already present at %s', rr_id, path_tails_hash)
        return response.text(
            'POST attached tails file {}, already present at {}'.format(rr_id, path_tails_hash),
            status=403)

    tsan = await MEM_CACHE.get('tsan')
    signature = request.files['signature'][0].body
    epoch_tails = '{}||{}'.format(epoch, request.files['tails-file'][0].body)
    if not tsan.verify(epoch_tails, signature, did):
        LOGGER.error('POST attached file %s failed to verify', tails_hash)
        return response.text('POST attached file {} failed to verify'.format(tails_hash), status=400)

    try:
        rev_reg_def = json.loads(await tsan.get_rev_reg_def(rr_id))
        ledger_hash = rev_reg_def.get('value', {}).get('tailsHash', None)
        if ledger_hash != tails_hash:
            LOGGER.error('POST attached tails file hash %s differs from ledger value %s', tails_hash, ledger_hash)
            return response.text(
                'POST attached tails file hash {} differs from ledger value {}'.format(tails_hash, ledger_hash),
                status=400)
    except AbsentRevReg:
        LOGGER.error('POST revocation registry not present on ledger for %s', rr_id)
        return response.text('POST revocation registry not present on ledger for {}'.format(rr_id), status=400)

    with open(path_tails_hash, 'wb') as fh_tails:
        fh_tails.write(request.files['tails-file'][0].body)

    Tails.associate(dir_tails, rr_id, tails_hash)
    LOGGER.info('Associated link %s to POST tails file attachment saved to %s', rr_id, path_tails_hash)

    return response.text('')
Ejemplo n.º 5
0
    async def create_rev_reg(self, rr_id: str, rr_size: int = None) -> None:
        """
        Create revocation registry artifacts and new tails file (with association to
        corresponding revocation registry identifier via symbolic link name)
        for input revocation registry identifier. Symbolic link presence signals completion.
        If revocation registry builder operates in a process external to its Issuer's,
        target directory is hopper directory.

        Raise WalletState for closed wallet.

        :param rr_id: revocation registry identifier
        :param rr_size: revocation registry size (defaults to 64)
        """

        LOGGER.debug('RevRegBuilder.create_rev_reg >>> rr_id: %s, rr_size: %s',
                     rr_id, rr_size)

        if not self.wallet.handle:
            LOGGER.debug(
                'RevRegBuilder.create_rev_reg <!< Wallet %s is closed',
                self.name)
            raise WalletState('Wallet {} is closed'.format(self.name))

        if not ok_rev_reg_id(rr_id):
            LOGGER.debug('RevRegBuilder.create_rev_reg <!< Bad rev reg id %s',
                         rr_id)
            raise BadIdentifier('Bad rev reg id {}'.format(rr_id))

        rr_size = rr_size or 64

        (cd_id, tag) = rev_reg_id2cred_def_id_tag(rr_id)

        dir_tails = self.dir_tails_top(rr_id)
        dir_target = self.dir_tails_target(rr_id)
        if self.external:
            try:
                makedirs(dir_target, exist_ok=False)
            except FileExistsError:
                LOGGER.warning(
                    'RevRegBuilder.create_rev_reg found dir %s, but task not in progress: rebuilding rev reg %s',
                    dir_target, rr_id)
                rmtree(dir_target)
                makedirs(dir_target, exist_ok=False)

        LOGGER.info(
            'Creating revocation registry (capacity %s) for rev reg id %s',
            rr_size, rr_id)
        tails_writer_handle = await blob_storage.open_writer(
            'default', json.dumps({
                'base_dir': dir_target,
                'uri_pattern': ''
            }))

        (created_rr_id, rr_def_json,
         rr_ent_json) = await anoncreds.issuer_create_and_store_revoc_reg(
             self.wallet.handle, self.did, 'CL_ACCUM', tag, cd_id,
             json.dumps({
                 'max_cred_num': rr_size,
                 'issuance_type': 'ISSUANCE_BY_DEFAULT'
             }), tails_writer_handle)

        tails_hash = basename(Tails.unlinked(dir_target).pop())
        with open(join(dir_target, 'rr_def.json'), 'w') as rr_def_fh:
            print(rr_def_json, file=rr_def_fh)
        with open(join(dir_target, 'rr_ent.json'), 'w') as rr_ent_fh:
            print(rr_ent_json, file=rr_ent_fh)
        Tails.associate(
            dir_tails, created_rr_id,
            tails_hash)  # associate last: symlink signals completion

        LOGGER.debug('RevRegBuilder.create_rev_reg <<<')
Ejemplo n.º 6
0
    async def _send_rev_reg_def(self, rr_id: str) -> None:
        """
        Move tails file from hopper; deserialize revocation registry definition and initial entry;
        send to ledger and cache revocation registry definition.

        Operation serializes to subdirectory within tails hopper directory; symbolic
        link presence signals completion.

        Raise AbsentRevReg if revocation registry is not ready in hopper, or AbsentTails if
        tails file is not yet linked by its revocation registry identifier.

        :param rr_id: revocation registry identifier
        """

        LOGGER.debug('Issuer._send_rev_reg_def >>> rr_id: %s', rr_id)

        dir_tails_rr_id = self.rrb.dir_tails_top(rr_id)
        dir_target = self.rrb.dir_tails_target(rr_id)

        if not Tails.linked(dir_tails_rr_id, rr_id):
            LOGGER.debug(
                'Issuer._send_rev_reg_def <!< Tails file for rev reg %s not ready in dir %s',
                rr_id, dir_target)
            raise AbsentRevReg(
                'Tails file for rev reg {} not ready in dir {}'.format(
                    rr_id, dir_target))

        file_rr_def = join(dir_target, 'rr_def.json')
        if not isfile(file_rr_def):
            LOGGER.debug(
                'Issuer._send_rev_reg_def <!< Rev reg def file %s not present',
                file_rr_def)
            raise AbsentRevReg(
                'Rev reg def file {} not present'.format(file_rr_def))
        with open(file_rr_def, 'r') as fh_rr_def:
            rr_def_json = fh_rr_def.read()

        file_rr_ent = join(dir_target, 'rr_ent.json')
        if not isfile(file_rr_ent):
            LOGGER.debug(
                'Issuer._send_rev_reg_def <!< Rev reg entry file %s not present',
                file_rr_ent)
            raise AbsentRevReg(
                'Rev reg entry file {} not present'.format(file_rr_ent))
        with open(file_rr_ent, 'r') as fh_rr_ent:
            rr_ent_json = fh_rr_ent.read()

        file_tails = Tails.linked(dir_tails_rr_id, rr_id)
        if not file_tails:
            LOGGER.debug(
                'Issuer._send_rev_reg_def <!< Tails link %s not present in dir %s',
                rr_id, dir_target)
            raise AbsentTails('Tails link {} not present in dir {}'.format(
                rr_id, dir_target))

        if self.rrbx:
            dir_cd_id = join(self.dir_tails, rev_reg_id2cred_def_id(rr_id))
            makedirs(dir_cd_id, exist_ok=True)
            rename(file_tails, join(dir_cd_id, basename(file_tails)))

        with REVO_CACHE.lock:
            rr_def_req_json = await ledger.build_revoc_reg_def_request(
                self.did, rr_def_json)
            await self._sign_submit(rr_def_req_json)
            await self.get_rev_reg_def(rr_id)  # add to cache en passant

        rr_ent_req_json = await ledger.build_revoc_reg_entry_request(
            self.did, rr_id, 'CL_ACCUM', rr_ent_json)
        await self._sign_submit(rr_ent_req_json)

        if self.rrbx:
            Tails.associate(self.dir_tails, rr_id, basename(file_tails))
            rmtree(dir_tails_rr_id)
        else:
            remove(file_rr_def)
            remove(file_rr_ent)

        LOGGER.debug('Issuer._send_rev_reg_def <<<')