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 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
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
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)
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)
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))
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'])
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
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
""" 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()))