Exemple #1
0
    def __init__(self,
                 character_flag: Union[ALICE, BOB],
                 verifying_key: Union[PublicKey, bytes],
                 encrypting_key: Optional[Union[PublicKey, bytes]] = None,
                 nickname: Optional[Union[bytes, str]] = None):

        try:
            self.__character_class = self.__CARD_TYPES[bytes(character_flag)]
        except KeyError:
            raise ValueError(f'Unsupported card type {str(character_flag)}')
        self.__character_flag = character_flag

        if isinstance(verifying_key, bytes):
            verifying_key = PublicKey.from_bytes(verifying_key)
        self.__verifying_key = verifying_key  # signing public key

        if isinstance(encrypting_key, bytes):
            encrypting_key = PublicKey.from_bytes(encrypting_key)
        self.__encrypting_key = encrypting_key  # public key

        if isinstance(nickname, str):
            nickname = nickname.encode()
        self.__nickname = nickname

        self.__validate()
Exemple #2
0
    def retrieve(self,
                 label: bytes,
                 policy_encrypting_key: bytes,
                 alice_verifying_key: bytes,
                 message_kit: bytes,
                 treasure_map: Union[bytes, str, 'TreasureMap'] = None):
        """
        Character control endpoint for re-encrypting and decrypting policy data.
        """
        from nucypher.characters.lawful import Enrico

        policy_encrypting_key = PublicKey.from_bytes(policy_encrypting_key)
        alice_verifying_key = PublicKey.from_bytes(alice_verifying_key)
        message_kit = UmbralMessageKit.from_bytes(
            message_kit
        )  # TODO #846: May raise UnknownOpenSSLError and InvalidTag.

        enrico = Enrico.from_public_keys(
            verifying_key=message_kit.sender_verifying_key,
            policy_encrypting_key=policy_encrypting_key,
            label=label)

        self.character.join_policy(label=label,
                                   alice_verifying_key=alice_verifying_key)

        plaintexts = self.character.retrieve(
            message_kit,
            enrico=enrico,
            alice_verifying_key=alice_verifying_key,
            label=label,
            treasure_map=treasure_map)

        response_data = {'cleartexts': plaintexts}
        return response_data
Exemple #3
0
 def convert(self, value, param, ctx):
     if self.validate:
         try:
             _key = PublicKey.from_bytes(bytes.fromhex(value))
         except (InternalError, ValueError):
             self.fail(f"'{value}' is not a valid nucypher public key.")
     return value
Exemple #4
0
 def __init__(self,
              public_key: PublicKey = None,
              keypair: keypairs.Keypair = None):
     if keypair and public_key:
         raise ValueError(
             "Pass keypair or pubkey_bytes (or neither), but not both.")
     elif keypair:
         self.keypair = keypair
     else:
         # They didn't pass a keypair; we'll make one with the bytes or
         # Umbral PublicKey if they provided such a thing.
         if public_key:
             try:
                 public_key = public_key.as_umbral_pubkey()
             except AttributeError:
                 try:
                     public_key = PublicKey.from_bytes(public_key)
                 except TypeError:
                     public_key = public_key
             self.keypair = self._keypair_class(public_key=public_key)
         else:
             # They didn't even pass a public key.  We have no choice but to generate a keypair.
             self.keypair = self._keypair_class(
                 generate_keys_if_needed=True)
Exemple #5
0
    def from_public_keys(cls,
                         powers_and_material: Dict = None,
                         verifying_key: Union[bytes, PublicKey] = None,
                         encrypting_key: Union[bytes, PublicKey] = None,
                         *args,
                         **kwargs) -> 'Character':
        """
        Sometimes we discover a Character and, at the same moment,
        learn the public parts of more of their powers. Here, we take a Dict
        (powers_and_material) in the format {CryptoPowerUp class: material},
        where material can be bytes or umbral.PublicKey.

        Each item in the collection will have the CryptoPowerUp instantiated
        with the given material, and the resulting CryptoPowerUp instance
        consumed by the Character.

        Alternatively, you can pass directly a verifying public key
        (for SigningPower) and/or an encrypting public key (for DecryptionPower).
        """
        crypto_power = CryptoPower()

        if powers_and_material is None:
            powers_and_material = dict()

        if verifying_key:
            powers_and_material[SigningPower] = verifying_key
        if encrypting_key:
            powers_and_material[DecryptingPower] = encrypting_key

        for power_up, public_key in powers_and_material.items():
            try:
                umbral_key = PublicKey.from_bytes(public_key)
            except TypeError:
                umbral_key = public_key

            crypto_power.consume_power_up(power_up(public_key=umbral_key))

        return cls(is_me=False, crypto_power=crypto_power, *args, **kwargs)
Exemple #6
0
class Card:
    """"
    A simple serializable representation of a character's public materials.
    """

    _alice_specification = dict(
        character_flag=(bytes, 8),
        verifying_key=(PublicKey, PublicKey.serialized_size()),
        nickname=(bytes, VariableLengthBytestring),
    )

    _bob_specification = dict(
        character_flag=(bytes, 8),
        verifying_key=(PublicKey, PublicKey.serialized_size()),
        encrypting_key=(PublicKey, PublicKey.serialized_size()),
        nickname=(bytes, VariableLengthBytestring),
    )

    __CARD_TYPES = {
        bytes(ALICE): Alice,
        bytes(BOB): Bob,
    }

    __ID_LENGTH = 10  # TODO: Review this size (bytes of hex len?)
    __MAX_NICKNAME_SIZE = 32
    __BASE_PAYLOAD_SIZE = sum(length[1]
                              for length in _bob_specification.values()
                              if isinstance(length[1], int))
    __MAX_CARD_LENGTH = __BASE_PAYLOAD_SIZE + __MAX_NICKNAME_SIZE + 2
    __FILE_EXTENSION = 'card'
    __DELIMITER = '.'  # delimits nickname from ID

    TRUNCATE = 16
    CARD_DIR = Path(DEFAULT_CONFIG_ROOT) / 'cards'
    NO_SIGNATURE.bool_value(False)

    class InvalidCard(Exception):
        """Raised when an invalid, corrupted, or otherwise unsable card is encountered"""

    class UnknownCard(Exception):
        """Raised when a card cannot be found in storage"""

    class UnsignedCard(Exception):
        """Raised when a card serialization cannot be handled due to the lack of a signature"""

    def __init__(self,
                 character_flag: Union[ALICE, BOB],
                 verifying_key: Union[PublicKey, bytes],
                 encrypting_key: Optional[Union[PublicKey, bytes]] = None,
                 nickname: Optional[Union[bytes, str]] = None):

        try:
            self.__character_class = self.__CARD_TYPES[bytes(character_flag)]
        except KeyError:
            raise ValueError(f'Unsupported card type {str(character_flag)}')
        self.__character_flag = character_flag

        if isinstance(verifying_key, bytes):
            verifying_key = PublicKey.from_bytes(verifying_key)
        self.__verifying_key = verifying_key  # signing public key

        if isinstance(encrypting_key, bytes):
            encrypting_key = PublicKey.from_bytes(encrypting_key)
        self.__encrypting_key = encrypting_key  # public key

        if isinstance(nickname, str):
            nickname = nickname.encode()
        self.__nickname = nickname

        self.__validate()

    def __repr__(self) -> str:
        name = self.nickname or f'{self.__character_class.__name__}'
        short_key = bytes(self.__verifying_key).hex()[:6]
        r = f'{self.__class__.__name__}({name}:{short_key}:{self.id.hex()[:6]})'
        return r

    def __eq__(self, other) -> bool:
        if not isinstance(other, self.__class__):
            raise TypeError(
                f'Cannot compare {self.__class__.__name__} and {other}')
        return self.id == other.id

    def __validate(self) -> bool:
        if self.__nickname and (len(self.__nickname) >
                                self.__MAX_NICKNAME_SIZE):
            raise self.InvalidCard(
                f'Nickname exceeds maximum length of {self.__MAX_NICKNAME_SIZE}'
            )
        return True

    @classmethod
    def __hash(cls, payload: bytes) -> HexBytes:
        blake = hashlib.blake2b()
        blake.update(payload)
        digest = blake.digest().hex()
        truncated_digest = digest[:cls.__ID_LENGTH]
        return HexBytes(truncated_digest)

    @property
    def character(self):
        return self.__CARD_TYPES[bytes(self.__character_flag)]

    #
    # Serializers
    #

    def __bytes__(self) -> bytes:
        self.__validate()
        payload = self.__payload
        if self.nickname:
            payload += VariableLengthBytestring(self.__nickname)
        return payload

    def __hex__(self) -> str:
        return self.to_hex()

    @property
    def __payload(self) -> bytes:
        elements = [
            self.__character_flag,
            self.__verifying_key,
        ]
        if self.character is Bob:
            elements.append(self.__encrypting_key)
        payload = b''.join(bytes(e) for e in elements)
        return payload

    @classmethod
    def from_bytes(cls, card_bytes: bytes) -> 'Card':
        if len(card_bytes) > cls.__MAX_CARD_LENGTH:
            raise cls.InvalidCard(
                f'Card exceeds maximum size (max is {cls.__MAX_CARD_LENGTH} bytes card is {len(card_bytes)} bytes). '
                f'Verify the card filepath and contents.')
        character_flag = card_bytes[:8]
        if character_flag == bytes(ALICE):
            specification = cls._alice_specification
        elif character_flag == bytes(BOB):
            specification = cls._bob_specification
        else:
            raise RuntimeError(
                f'Unknown character card header ({character_flag}).')
        return BytestringKwargifier(cls, **specification)(card_bytes)

    @classmethod
    def from_hex(cls, hexdata: str):
        return cls.from_bytes(bytes.fromhex(hexdata))

    def to_hex(self) -> str:
        return bytes(self).hex()

    @classmethod
    def from_base64(cls, b64data: str):
        return cls.from_bytes(base64.urlsafe_b64decode(b64data))

    def to_base64(self) -> str:
        return base64.urlsafe_b64encode(bytes(self)).decode()

    def to_qr_code(self):
        import qrcode
        from qrcode.main import QRCode
        qr = QRCode(
            version=1,
            box_size=1,
            border=4,  # min spec is 4
            error_correction=qrcode.constants.ERROR_CORRECT_L,
        )
        qr.add_data(bytes(self))
        qr.print_ascii()

    @classmethod
    def from_dict(cls, card: Dict):
        instance = cls(nickname=card.get('nickname'),
                       verifying_key=card['verifying_key'],
                       encrypting_key=card['encrypting_key'],
                       character_flag=card['character'])
        return instance

    def to_dict(self) -> Dict:
        payload = dict(nickname=self.__nickname,
                       verifying_key=self.verifying_key,
                       encrypting_key=self.encrypting_key,
                       character=self.__character_flag)
        return payload

    def describe(self, truncate: int = TRUNCATE) -> Dict:
        description = dict(nickname=self.__nickname,
                           id=self.id.hex(),
                           verifying_key=bytes(
                               self.verifying_key).hex()[:truncate],
                           character=self.character.__name__)
        if self.character is Bob:
            description['encrypting_key'] = bytes(
                self.encrypting_key).hex()[:truncate]
        return description

    def to_json(self, as_string: bool = True) -> Union[dict, str]:
        payload = dict(nickname=self.__nickname.decode(),
                       verifying_key=bytes(self.verifying_key).hex(),
                       encrypting_key=bytes(self.encrypting_key).hex(),
                       character=self.character.__name__)
        if as_string:
            payload = json.dumps(payload)
        return payload

    @classmethod
    def from_character(cls,
                       character: Character,
                       nickname: Optional[str] = None) -> 'Card':
        flag = getattr(constant_sorrow.constants,
                       character.__class__.__name__.upper())
        instance = cls(
            verifying_key=character.public_keys(power_up_class=SigningPower),
            encrypting_key=character.public_keys(
                power_up_class=DecryptingPower),
            character_flag=bytes(flag),
            nickname=nickname)
        return instance

    #
    # Card API
    #

    @property
    def verifying_key(self) -> PublicKey:
        return self.__verifying_key

    @property
    def encrypting_key(self) -> PublicKey:
        return self.__encrypting_key

    @property
    def id(self) -> HexBytes:
        return self.__hash(self.__payload)

    @property
    def nickname(self) -> str:
        if self.__nickname:
            return self.__nickname.decode()

    def set_nickname(self, nickname: str) -> None:
        nickname = nickname.replace(' ', '_')
        if len(nickname.encode()) > self.__MAX_NICKNAME_SIZE:
            raise ValueError(
                f'New nickname exceeds maximum size ({self.__MAX_NICKNAME_SIZE} bytes)'
            )
        self.__nickname = nickname.encode()

    @nickname.setter
    def nickname(self, nickname: str) -> None:
        self.set_nickname(nickname)

    #
    # Card Storage API
    #

    @property
    def filepath(self) -> Path:
        identifier = f'{self.nickname}{self.__DELIMITER}{self.id.hex()}' if self.__nickname else self.id.hex(
        )
        filename = f'{identifier}.{self.__FILE_EXTENSION}'
        filepath = self.CARD_DIR / filename
        return filepath

    @property
    def is_saved(self) -> bool:
        exists = self.filepath.exists()
        return exists

    def save(self,
             encoder: Callable = base64.b64encode,
             overwrite: bool = False) -> Path:
        if not self.CARD_DIR.exists():
            os.mkdir(str(self.CARD_DIR))
        if self.is_saved and not overwrite:
            raise FileExistsError(
                'Card exists. Pass overwrite=True to allow this operation.')
        with open(str(self.filepath), 'wb') as file:
            file.write(encoder(bytes(self)))
        return Path(self.filepath)

    @classmethod
    def lookup(cls,
               identifier: str,
               card_dir: Optional[Path] = CARD_DIR) -> Path:
        """Resolve a card ID or nickname into a Path object"""
        try:
            nickname, _id = identifier.split(cls.__DELIMITER)
        except ValueError:
            nickname = identifier
        filenames = [
            f for f in os.listdir(Card.CARD_DIR)
            if nickname.lower() in f.lower()
        ]
        if not filenames:
            raise cls.UnknownCard(f'Unknown card nickname or ID "{nickname}".')
        elif len(filenames) == 1:
            filename = filenames[0]
        else:
            raise ValueError(
                f'Ambiguous card nickname: {nickname}. Try using card ID instead.'
            )
        filepath = card_dir / filename
        return filepath

    @classmethod
    def load(cls,
             filepath: Optional[Path] = None,
             identifier: str = None,
             card_dir: Path = None,
             decoder: Callable = base64.b64decode) -> 'Card':

        if not card_dir:
            card_dir = cls.CARD_DIR
        if filepath and identifier:
            raise ValueError(f'Pass either filepath or identifier, not both.')
        if not filepath:
            filepath = cls.lookup(identifier=identifier, card_dir=card_dir)
        try:
            with open(str(filepath), 'rb') as file:
                card_bytes = decoder(file.read())
        except FileNotFoundError:
            raise cls.UnknownCard
        instance = cls.from_bytes(card_bytes)
        return instance

    def delete(self) -> None:
        os.remove(str(self.filepath))
Exemple #7
0
    federated_only=True,
    crypto_power_ups=power_ups,
    start_learning_now=True,
    abort_on_learning_error=True,
    known_nodes=[ursula],
    save_metadata=False,
    network_middleware=RestMiddleware(),
)

print("Doctor = ", doctor)

# Let's join the policy generated by Alicia. We just need some info about it.
with open("policy-metadata.json", 'r') as f:
    policy_data = json.load(f)

policy_pubkey = PublicKey.from_bytes(
    bytes.fromhex(policy_data["policy_pubkey"]))
alices_sig_pubkey = PublicKey.from_bytes(
    bytes.fromhex(policy_data["alice_sig_pubkey"]))
label = policy_data["label"].encode()

print("The Doctor joins policy for label '{}'".format(label.decode("utf-8")))
doctor.join_policy(label, alices_sig_pubkey)

# Now that the Doctor joined the policy in the NuCypher network,
# he can retrieve encrypted data which he can decrypt with his private key.
# But first we need some encrypted data!
# Let's read the file produced by the heart monitor and unpack the MessageKits,
# which are the individual ciphertexts.
data = msgpack.load(open("heart_data.msgpack", "rb"), raw=False)
message_kits = (UmbralMessageKit.from_bytes(k) for k in data['kits'])
Exemple #8
0
 def encrypting_public_key(self):
     encrypting_pubkey_bytes = _read_keyfile(
         keypath=self.__root_pub_keypath, deserializer=None)
     encrypting_pubkey = PublicKey.from_bytes(encrypting_pubkey_bytes)
     return encrypting_pubkey
Exemple #9
0
 def signing_public_key(self):
     signature_pubkey_bytes = _read_keyfile(
         keypath=self.__signing_pub_keypath, deserializer=None)
     signature_pubkey = PublicKey.from_bytes(signature_pubkey_bytes)
     return signature_pubkey
Exemple #10
0
"""
This file is part of nucypher.

nucypher is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

nucypher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with nucypher.  If not, see <https://www.gnu.org/licenses/>.
"""

from bytestring_splitter import BytestringSplitter, VariableLengthBytestring
from nucypher.crypto.umbral_adapter import CapsuleFrag, PublicKey, Capsule, Signature

key_splitter = BytestringSplitter((PublicKey, PublicKey.serialized_size()))
capsule_splitter = BytestringSplitter((Capsule, Capsule.serialized_size()))
cfrag_splitter = BytestringSplitter(
    (CapsuleFrag, CapsuleFrag.serialized_size()))
signature_splitter = BytestringSplitter(
    (Signature, Signature.serialized_size()))