示例#1
0
def test_character_card(character_class, capsys):
    character = character_class(federated_only=True,
                                start_learning_now=False,
                                network_middleware=MockRestMiddleware())

    character_card = character.get_card()
    same_card = Card.from_character(character)
    assert character_card == same_card

    with pytest.raises(TypeError):
        # only cards can be compared to other cards
        _ = character_card == same_card.verifying_key

    # Bob's Keys
    assert character_card.verifying_key == character.public_keys(SigningPower)
    assert character_card.encrypting_key == character.public_keys(
        DecryptingPower)

    # Card Serialization

    # bytes
    card_bytes = bytes(character_card)
    assert Card.from_bytes(card_bytes) == character_card == same_card

    # hex
    hex_bob = character_card.to_hex()
    assert Card.from_hex(hex_bob) == character_card == same_card

    # base64
    base64_bob = character_card.to_base64()
    assert Card.from_base64(base64_bob) == character_card == same_card

    # qr code echo
    character_card.to_qr_code()
    captured = capsys.readouterr()
    qr_code_padding = '\xa0' * 21  # min length for qr code version 1
    assert captured.out.startswith(qr_code_padding)
    assert captured.out.endswith(f'{qr_code_padding}\n')

    # filepath without nickname
    assert character_card.id.hex() in str(character_card.filepath)

    # nicknames
    original_checksum = character_card.id
    nickname = 'Wilson the Great'
    expected_nickname = nickname.replace(' ', '_')
    character_card.set_nickname(nickname)
    restored = Card.from_bytes(bytes(character_card))
    restored_checksum = restored.id
    assert restored.nickname == expected_nickname
    assert original_checksum == restored_checksum == same_card.id

    # filepath with nickname
    assert f'{expected_nickname}.{character_card.id.hex()}' in str(
        character_card.filepath)
示例#2
0
def paint_single_card(emitter, card: Card, qrcode: bool = False) -> None:
    emitter.echo('*' * 90)
    emitter.message(
        f'{(card.nickname or str(card.character.__name__)).capitalize()}\'s Card ({card.id.hex()})'
    )
    emitter.echo(f'Verifying Key - {card.verifying_key.hex()}')
    if card.character is Bob:
        emitter.echo(f'Encrypting Key - {card.encrypting_key.hex()}')
    if qrcode:
        card.to_qr_code()
    emitter.echo('*' * 90)
示例#3
0
def create(character_flag, verifying_key, encrypting_key, nickname, force):
    """Store a new character card"""
    emitter = StdoutEmitter()

    # Validate
    if not all((character_flag, verifying_key, encrypting_key)) and force:
        emitter.error(
            f'--verifying-key, --encrypting-key, and --type are required with --force enabled.'
        )

    # Card type
    from constant_sorrow.constants import ALICE, BOB
    flags = {'a': ALICE, 'b': BOB}
    if not character_flag:
        choice = click.prompt('Enter Card Type - (A)lice or (B)ob',
                              type=click.Choice(['a', 'b'],
                                                case_sensitive=False))
        character_flag = flags[choice]
    else:
        character_flag = flags[character_flag]

    # Verifying Key
    if not verifying_key:
        verifying_key = click.prompt('Enter Verifying Key',
                                     type=UMBRAL_PUBLIC_KEY_HEX)
    verifying_key = bytes.fromhex(verifying_key)  # TODO: Move / Validate

    # Encrypting Key
    if character_flag is BOB:
        if not encrypting_key:
            encrypting_key = click.prompt('Enter Encrypting Key',
                                          type=UMBRAL_PUBLIC_KEY_HEX)
        encrypting_key = bytes.fromhex(encrypting_key)  # TODO: Move / Validate

    # Init
    new_card = Card(character_flag=character_flag,
                    verifying_key=verifying_key,
                    encrypting_key=encrypting_key,
                    nickname=nickname)

    # Nickname
    if not force and not nickname:
        card_id_hex = new_card.id.hex()
        nickname = click.prompt('Enter nickname for card', default=card_id_hex)
        if nickname != card_id_hex:  # not the default
            nickname = nickname.strip()
            new_card.nickname = nickname

    # Save
    new_card.save()
    emitter.message(f'Saved new card {new_card}', color='green')
    paint_single_card(emitter=emitter, card=new_card)
示例#4
0
def select_card(emitter, card_identifier: str) -> Card:
    if not card_identifier:
        cards = []
        for filename in os.listdir(Card.CARD_DIR):
            filepath = Card.CARD_DIR / filename
            card = Card.load(filepath=filepath)
            cards.append(card)
        paint_cards(emitter=emitter, cards=cards, as_table=True)
        selection = click.prompt('Select card',
                                 type=click.IntRange(0, len(cards)))
        card = cards[selection]
    else:
        card = Card.load(identifier=card_identifier)
    return card
示例#5
0
def make_card(general_config, character_options, config_file, nickname):
    emitter = setup_emitter(general_config)
    BOB = character_options.create_character(emitter, config_file, json_ipc=False)
    card = Card.from_character(BOB)
    if nickname:
        card.nickname = nickname
    card.save(overwrite=True)
    emitter.message(f"Saved new character card to {card.filepath}", color='green')
    paint_single_card(card=card, emitter=emitter)
示例#6
0
def test_character_card(character_class):
    character = character_class(federated_only=True,
                                start_learning_now=False,
                                network_middleware=MockRestMiddleware())

    character_card = character.get_card()
    same_card = Card.from_character(character)
    assert character_card == same_card

    with pytest.raises(TypeError):
        # only cards can be compared to other cards
        _ = character_card == same_card.verifying_key

    # Bob's Keys
    assert character_card.verifying_key == character.public_keys(SigningPower)
    assert character_card.encrypting_key == character.public_keys(
        DecryptingPower)

    # Card Serialization

    # bytes
    card_bytes = bytes(character_card)
    assert Card.from_bytes(card_bytes) == character_card == same_card

    # hex
    hex_bob = character_card.to_hex()
    assert Card.from_hex(hex_bob) == character_card == same_card

    # base64
    base64_bob = character_card.to_base64()
    assert Card.from_base64(base64_bob) == character_card == same_card

    # qr code echo
    character_card.to_qr_code()
    # TODO: Examine system output here?

    # nicknames
    original_checksum = character_card.id
    nickname = 'Wilson'
    character_card.set_nickname(nickname)
    restored = Card.from_bytes(bytes(character_card))
    restored_checksum = restored.id
    assert restored.nickname == nickname
    assert original_checksum == restored_checksum == same_card.id
示例#7
0
def make_card(general_config, character_options, config_file, nickname):
    """Create a character card file for public key sharing"""
    emitter = setup_emitter(general_config)
    ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc, load_seednodes=False)
    card = Card.from_character(ALICE)
    if nickname:
        card.nickname = nickname
    card.save(overwrite=True)
    emitter.message(f"Saved new character card to {card.filepath}", color='green')
    paint_single_card(card=card, emitter=emitter)
示例#8
0
文件: bob.py 项目: vschinzel/nucypher
def retrieve(general_config,
             character_options,
             config_file,
             label,
             policy_encrypting_key,
             alice_verifying_key,
             message_kit,
             ipfs,
             alice,
             force):
    """Obtain plaintext from encrypted data, if access was granted."""

    # Setup
    emitter = setup_emitter(general_config)
    BOB = character_options.create_character(emitter, config_file)

    if ipfs:
        import ipfshttpclient
        # TODO: #2108
        emitter.message(f"Connecting to IPFS Gateway {ipfs}")
        ipfs_client = ipfshttpclient.connect(ipfs)
        cid = message_kit  # Understand the message kit value as an IPFS hash.
        raw_message_kit = ipfs_client.cat(cid)  # cat the contents at the hash reference
        emitter.message(f"Downloaded message kit from IPFS (CID {cid})", color='green')
        message_kit = raw_message_kit.decode()  # cast to utf-8

    if not alice_verifying_key:
        if alice:  # from storage
            card = Card.load(identifier=alice)
            if card.character is not Alice:
                emitter.error('Grantee card is not an Alice.')
                raise click.Abort
            alice_verifying_key = card.verifying_key.hex()
            emitter.message(f'{card.nickname or ("Alice #"+card.id.hex())}\n'
                            f'Verifying Key  | {card.verifying_key.hex()}',
                            color='green')
            if not force:
                click.confirm('Is this the correct Granter (Alice)?', abort=True)
        else:  # interactive
            alice_verifying_key = click.prompt("Enter Alice's verifying key")

    # Request
    bob_request_data = {
        'label': label,
        'policy_encrypting_key': policy_encrypting_key,
        'alice_verifying_key': alice_verifying_key,
        'message_kit': message_kit,
    }

    response = BOB.controller.retrieve(request=bob_request_data)
    return response
示例#9
0
def _list():
    """Show all character cards"""
    emitter = StdoutEmitter()
    if not Card.CARD_DIR.is_dir():
        Card.CARD_DIR.mkdir()
    card_filepaths = list(Card.CARD_DIR.iterdir())
    if not card_filepaths:
        emitter.error(
            f'No cards found at {Card.CARD_DIR}.  '
            f"To create one run 'nucypher {contacts.name} {create.name}'.")
    cards = list()
    for filename in card_filepaths:
        card = Card.load(filepath=Card.CARD_DIR / filename)
        cards.append(card)
    paint_cards(emitter=emitter, cards=cards, as_table=True)
示例#10
0
def _list():
    """Show all character cards"""
    emitter = StdoutEmitter()
    card_directory = Card.CARD_DIR
    try:
        card_filepaths = os.listdir(card_directory)
    except FileNotFoundError:
        os.mkdir(Card.CARD_DIR)
        card_filepaths = os.listdir(card_directory)
    if not card_filepaths:
        emitter.error(
            f'No cards found at {card_directory}.  '
            f"To create one run 'nucypher {contacts.name} {create.name}'.")
    cards = list()
    for filename in card_filepaths:
        card = Card.load(filepath=Card.CARD_DIR / filename)
        cards.append(card)
    paint_cards(emitter=emitter, cards=cards, as_table=True)
示例#11
0
def collect_keys_from_card(emitter: StdoutEmitter, card_identifier: str,
                           force: bool):
    emitter.message(f"Searching contacts for {card_identifier}\n",
                    color='yellow')
    card = Card.load(identifier=card_identifier)

    if card.character is not Bob:
        emitter.error('Grantee card is not a Bob.')
        raise click.Abort
    paint_single_card(emitter=emitter, card=card)

    if not force:
        click.confirm('Is this the correct grantee (Bob)?', abort=True)

    bob_encrypting_key = bytes(card.encrypting_key).hex()
    bob_verifying_key = bytes(card.verifying_key).hex()
    public_keys = PublicKeys(encrypting_key=bob_encrypting_key,
                             verifying_key=bob_verifying_key)
    return public_keys
示例#12
0
def retrieve_and_decrypt(general_config, character_options, config_file,
                         alice_verifying_key, treasure_map, message_kit, ipfs,
                         alice, decode, force):
    """Obtain plaintext from encrypted data, if access was granted."""
    # 'message_kit' is a required and a "multiple" value click option  - the option name was kept singular so that
    # it makes sense when specifying many of them i.e. `--message-kit <message_kit_1> --message-kit <message_kit_2> ...`
    message_kits = list(message_kit)

    # Setup
    emitter = setup_emitter(general_config)
    BOB = character_options.create_character(emitter,
                                             config_file,
                                             json_ipc=general_config.json_ipc)

    if not (bool(alice_verifying_key) ^ bool(alice)):
        message = f"Pass either '--alice_verifying_key' or '--alice'; " \
                  f"got {'both' if alice_verifying_key else 'neither'}"
        raise click.BadOptionUsage(
            option_name='--alice_verifying_key, --alice', message=message)

    if not alice_verifying_key:
        if alice:  # from storage
            card = Card.load(identifier=alice)
            if card.character is not Alice:
                emitter.error('Grantee card is not an Alice.')
                raise click.Abort
            alice_verifying_key = bytes(card.verifying_key).hex()
            emitter.message(
                f'{card.nickname or ("Alice #"+card.id.hex())}\n'
                f'Verifying Key  | {bytes(card.verifying_key).hex()}',
                color='green')
            if not force:
                click.confirm('Is this the correct Granter (Alice)?',
                              abort=True)

    if ipfs:
        # '--message_kit' option was repurposed to specify ipfs cids (#2098)
        cids = []
        for cid in message_kits:
            cids.append(cid)

        # populate message_kits list with actual message_kits
        message_kits = []
        import ipfshttpclient
        # TODO: #2108
        emitter.message(f"Connecting to IPFS Gateway {ipfs}")
        ipfs_client = ipfshttpclient.connect(ipfs)
        for cid in cids:
            raw_message_kit = ipfs_client.cat(
                cid)  # cat the contents at the hash reference
            emitter.message(f"Downloaded message kit from IPFS (CID {cid})",
                            color='green')
            message_kit = raw_message_kit.decode()  # cast to utf-8
            message_kits.append(message_kit)

    # Request
    bob_request_data = {
        'alice_verifying_key': alice_verifying_key,
        'message_kits': message_kits,
        'encrypted_treasure_map': treasure_map
    }

    response = BOB.controller.retrieve_and_decrypt(request=bob_request_data)
    if decode:
        messages = list(
            [b64decode(r).decode() for r in response['cleartexts']])
        emitter.echo('----------Messages----------')
        for message in messages:
            emitter.echo(message)
    return response
示例#13
0
def retrieve(general_config,
             character_options,
             config_file,
             label,
             policy_encrypting_key,
             alice_verifying_key,
             message_kit,
             ipfs,
             alice,
             decode,
             force):
    """Obtain plaintext from encrypted data, if access was granted."""

    # Setup
    emitter = setup_emitter(general_config)
    BOB = character_options.create_character(emitter, config_file, json_ipc=general_config.json_ipc)

    if not message_kit:
        if ipfs:
            prompt = "Enter IPFS CID for encrypted data"
        else:
            prompt = "Enter encrypted data (base64)"
        message_kit = click.prompt(prompt, type=click.STRING)

    if ipfs:
        import ipfshttpclient
        # TODO: #2108
        emitter.message(f"Connecting to IPFS Gateway {ipfs}")
        ipfs_client = ipfshttpclient.connect(ipfs)
        cid = message_kit  # Understand the message kit value as an IPFS hash.
        raw_message_kit = ipfs_client.cat(cid)  # cat the contents at the hash reference
        emitter.message(f"Downloaded message kit from IPFS (CID {cid})", color='green')
        message_kit = raw_message_kit.decode()  # cast to utf-8

    if not alice_verifying_key:
        if alice:  # from storage
            card = Card.load(identifier=alice)
            if card.character is not Alice:
                emitter.error('Grantee card is not an Alice.')
                raise click.Abort
            alice_verifying_key = bytes(card.verifying_key).hex()
            emitter.message(f'{card.nickname or ("Alice #"+card.id.hex())}\n'
                            f'Verifying Key  | {bytes(card.verifying_key).hex()}',
                            color='green')
            if not force:
                click.confirm('Is this the correct Granter (Alice)?', abort=True)
        else:  # interactive
            alice_verifying_key = click.prompt("Enter Alice's verifying key", click.STRING)

    if not force:
        if not policy_encrypting_key:
            policy_encrypting_key = click.prompt("Enter policy public key", type=click.STRING)

        if not label:
            label = click.prompt("Enter label to retrieve", type=click.STRING)

    # Request
    bob_request_data = {
        'label': label,
        'policy_encrypting_key': policy_encrypting_key,
        'alice_verifying_key': alice_verifying_key,
        'message_kit': message_kit,
    }

    response = BOB.controller.retrieve(request=bob_request_data)
    if decode:
        messages = list([b64decode(r).decode() for r in response['cleartexts']])
        emitter.echo('----------Messages----------')
        for message in messages:
            emitter.echo(message)
    return response
示例#14
0
def grant(general_config,
          bob,
          bob_encrypting_key,
          bob_verifying_key,
          label,
          value,
          rate,
          expiration,
          m, n,
          character_options,
          config_file,
          force):
    """Create and enact an access policy for some Bob. """

    # Setup
    emitter = setup_emitter(general_config)
    ALICE = character_options.create_character(emitter, config_file, general_config.json_ipc)

    # Policy option validation
    if ALICE.federated_only:
        if any((value, rate)):
            message = "Can't use --value or --rate with a federated Alice."
            raise click.BadOptionUsage(option_name="--value, --rate", message=message)
    elif bool(value) and bool(rate):
        raise click.BadOptionUsage(option_name="--rate", message="Can't use --value if using --rate")

    # Grantee selection
    if bob and any((bob_encrypting_key, bob_verifying_key)):
        message = '--bob cannot be used with --bob-encrypting-key or --bob-veryfying key'
        raise click.BadOptionUsage(option_name='--bob', message=message)

    if bob:
        card = Card.load(identifier=bob)
        if card.character is not Bob:
            emitter.error('Grantee card is not a Bob.')
            raise click.Abort
        paint_single_card(emitter=emitter, card=card)
        if not force:
            click.confirm('Is this the correct grantee (Bob)?', abort=True)
        bob_encrypting_key = card.encrypting_key.hex()
        bob_verifying_key = card.verifying_key.hex()

    # Interactive collection follows:
    # TODO: Extricate to support modules
    # - Disclaimer
    # - Label
    # - Expiration Date & Time
    # - M of N
    # - Policy Value (ETH)

    # Policy Expiration
    # TODO: Remove this line when the time is right.
    paint_probationary_period_disclaimer(emitter)

    # Label
    if not label:
        label = click.prompt(f'Enter label to grant Bob {bob_verifying_key[:8]}', type=click.STRING)

    if not force and not expiration:
        if ALICE.duration_periods:
            # TODO: use a default in days or periods?
            expiration = maya.now() + timedelta(days=ALICE.duration_periods)  # default
            if not click.confirm(f'Use default policy duration (expires {expiration})?'):
                expiration = click.prompt('Enter policy expiration datetime', type=click.DateTime())
        else:
            # No policy duration default default available; Go interactive
            expiration = click.prompt('Enter policy expiration datetime', type=click.DateTime())

    # TODO: Remove this line when the time is right.
    enforce_probationary_period(emitter=emitter, expiration=expiration)

    # Policy Threshold and Shares
    if not n:
        n = ALICE.n
        if not force and not click.confirm(f'Use default value for N ({n})?', default=True):
            n = click.prompt('Enter total number of shares (N)', type=click.INT)
    if not m:
        m = ALICE.m
        if not force and not click.confirm(f'Use default value for M ({m})?', default=True):
            m = click.prompt('Enter threshold (M)', type=click.IntRange(1, n))

    # Policy Value
    policy_value_provided = bool(value) or bool(rate)
    if not ALICE.federated_only and not policy_value_provided:
        rate = ALICE.default_rate  # TODO #1709 - Fine tuning and selection of default rates
        if not force:
            default_gwei = Web3.fromWei(rate, 'gwei')
            prompt = "Confirm rate of {node_rate} gwei ({total_rate} gwei per period)?"
            if not click.confirm(prompt.format(node_rate=default_gwei, total_rate=default_gwei*n), default=True):
                interactive_rate = click.prompt('Enter rate per period in gwei', type=GWEI)
                # TODO: Validate interactively collected rate (#1709)
                click.confirm(prompt.format(node_rate=rate, total_rate=rate*n), default=True, abort=True)
                rate = Web3.toWei(interactive_rate, 'gwei')

    # Request
    grant_request = {
        'bob_encrypting_key': bob_encrypting_key,
        'bob_verifying_key': bob_verifying_key,
        'label': label,
        'm': m,
        'n': n,
        'expiration': expiration,
    }
    if not ALICE.federated_only:
        if value:
            grant_request['value'] = value
        elif rate:
            grant_request['rate'] = rate  # in wei

    if not force and not general_config.json_ipc:
        confirm_staged_grant(emitter=emitter, grant_request=grant_request)
    emitter.echo(f'Granting Access to {bob_verifying_key[:8]}', color='yellow')
    return ALICE.controller.grant(request=grant_request)