def cert_request(self, key_id: str, uid: str) -> PGPKey: key = self.personal_keystore.get_key(key_id) if key is None: raise CustomException("No key with requested key_id found.") key = self._extract_matching_uid(key, uid) if key is None: raise CustomException( "Requested key does not have the requested uid.") return key
def cert_import_single(self, key: PGPKey) -> None: if len(key.userids) != 1: raise CustomException( "Certification contained more than one UserID.") uid: PGPUID = key.userids[0] if len(uid.__sig__) > 2: # self-signature plus one certification logging.error(uid.__sig__) raise CustomException("More than one certification included") if not key.verify(uid): raise CustomException( "UserID was not self-signed. This UID does not belong on this key." ) self.personal_keystore.add_userid(key, uid)
def use_identity(self, key_id: str, reuse: bool = False) -> None: key = self.personal_keystore.get_key(key_id) if key is None: raise CustomException("Public Key with this ID not available") key = self.personal_keystore.get_private_key(key.fingerprint) if key is None: raise CustomException("Private Key with this ID not available") if self.personal_keystore.is_expended(key.fingerprint) and not reuse: raise CustomException( "Identity is marked as expended/previously used and reuse was not allowed. " "Use --reuse to explicitly allow it.") identity_name, identity_mail = identity_from_keyid(key_id) self.git_config.set_user_identity(identity_name, identity_mail, key_id)
def _combine_key_parts(key_parts: List[PGPKey]) -> PGPKey: # pylint: disable=protected-access final_key = None for part in key_parts: if part is None: continue if final_key is None: # first key part final_key = copy(part) continue if not part.fingerprint == final_key.fingerprint: raise CustomException( "Fingerprints don't match for key parts to be combined.") if final_key.is_public and not part.is_public: old_final_key = copy(final_key) # noinspection PyProtectedMember final_key._key = copy(part._key) final_key.pubkey = old_final_key for uid in part.userids: final_key |= uid for sig in part.__sig__: final_key |= sig for subkey in part.subkeys: final_key |= subkey return final_key
def extract_keyid_from_identity( identity: str) -> Tuple[str, Optional[pgpy.pgp.Fingerprint]]: regex = "^(ANON )?(?P<keyid>[1234567890abcdefABCDEF]+) ?.*$" match = re.match(regex, identity) if match is None: raise CustomException( "Not a git-anon identity. Format does not match.") keyid: str = match.groupdict()["keyid"] keyid_len = len(keyid) if keyid_len not in (16, 40): # keyid is invalid raise CustomException("Not a git-anon identity. Invalid length: ", keyid_len) if keyid_len == 40: return keyid[-16:], pgpy.pgp.Fingerprint(keyid) return keyid[-16:], None
def cert_trust(input_file: TextIOWrapper) -> None: # todo require user to provide all acceptable uids as parameters for key in _parse_keys_from_list_of_lines(input_file.readlines()): if not key.is_public and key.is_protected: raise CustomException( "Encrypted/Protected keys are not supported. Remove protection before importing." ) API.cert_import_certification_key(key)
def reveal_identity(self, key_id: str, encrypted: bool) -> List[str]: key = self.personal_keystore.get_key(key_id) if key is None: raise CustomException("No identity with given id could be found.") revealed_uids = [] for uid in key.userids: self.shared_keystore.add_userid(key, uid, encrypted) revealed_uids.append(uid_as_str(uid)) return revealed_uids
def get_private_key(self, fingerprint: str) -> Optional[PGPKey]: expected_fingerprint = Fingerprint(fingerprint) key_file = self._get_private_key_location(expected_fingerprint) try: key, _ = PGPKey.from_file(key_file) except FileNotFoundError: return None if not key.fingerprint == expected_fingerprint: raise CustomException("Actual fingerprint for stored private key does not match expected one. ({})".format( expected_fingerprint)) self._strip_signatures_uids_subkeys(key) pubkey = self.get_key(key.fingerprint.keyid, key.fingerprint) if pubkey is None: raise CustomException( "Missing public key for fingerprint {}, while private is available".format(expected_fingerprint)) return self._combine_key_parts([pubkey, key])
def unmask_identity( self, anonymous_identity: str) -> Tuple[List[str], List[str]]: keyid, fingerprint = extract_keyid_from_identity(anonymous_identity) key = self.shared_keystore.get_key(keyid, fingerprint) if key is None: raise CustomException("Unknown Identity: Public Key not found.") uids = key.userids if len(uids) == 0: # this would make the identity malformed as it has to have at least a pseudo uid. raise CustomException("Unknown Identity: Identity has no UserIDs") unverified_attributes = [] verified_attributes = [] for uid in uids: if verify_uid(uid, self.trusted_keystore): verified_attributes.append(uid_as_str(uid)) else: unverified_attributes.append(uid_as_str(uid)) return verified_attributes, unverified_attributes
def reveal_attribute(self, key_id: str, attribute: str, encrypted: bool = True) -> List[str]: key = self.personal_keystore.get_key(key_id) if key is None: raise CustomException("No identity with given id could be found.") revealed_uids = [] for uid in key.userids: if uid_as_str(uid) == attribute: self.shared_keystore.add_userid(key, uid, encrypted) revealed_uids.append(uid_as_str(uid)) # This also accepts previously revealed user ids and reveals them again. # (which should not create a new file) if len(revealed_uids) == 0: raise CustomException( "This attribute has not been added for this identity. " "Please add it before attempting to reveal it.") return revealed_uids
def _write_file(self, filename: str, data: bytes) -> None: if os.path.isfile(filename): # We already stored this exact serialized packet. with open(filename, "rb") as file: if not file.read() == data: raise CustomException( "Writing to {} failed. File already exists but contents are not as expected." .format(filename)) return with open(filename, "wb") as file: file.write(data)
def cert_gen_key(self, uids: List[str]) -> Tuple[PGPKey, PGPKey]: pgpy_uids = [] for uid in uids: pgpy_uid = parse_uid(uid) if pgpy_uid is None: raise CustomException( "UID '{}' could not be parsed".format(uid)) pgpy_uids.append(pgpy_uid) pub, sec = create_identity_internal(None, pgpy_uids) self.trusted_keystore.store_key(sec) return pub, sec
def _get_primary_key( self, keyid: str, fingerprint: Optional[pgpy.pgp.Fingerprint]) -> Optional[PGPKey]: possible_location: str if fingerprint is not None: if fingerprint.keyid != keyid: raise CustomException( "Keyid does not match with end of provided fingerprint.") possible_location = self._folder_for_fingerprint(fingerprint) else: potential_locations = self._folders_for_keyid(keyid) if len(potential_locations) == 0: return None if len(potential_locations) > 1: message = "Found more than one key with id " + keyid + ": keys:" for potential_location in potential_locations: fpr = potential_location[:-40] message += "\n fpr:" + fpr message += "Consider yourself lucky. This might be a sensation and is so unlikely that no code was " \ "written to handle this situation." raise CustomException(message) possible_location = potential_locations[0] if not os.path.isdir(possible_location): # the requested key is not available return None primary_key_file = os.path.join(possible_location, "primary_key.pub") if not os.path.isfile(primary_key_file): # the folder exists but the primary key does not return None key, _ = pgpy.PGPKey.from_file(primary_key_file) if not possible_location == self._folder_for_fingerprint( key.fingerprint): # the fingerprint does not match the storage location return None return self._verify_primary_key(key, keyid, fingerprint, self._expected_pseudo_uid())
def store_key(self, key: PGPKey) -> None: key = copy(key) self.add_key_and_all_uids(key) key_file = self._get_private_key_location(key.fingerprint) self._strip_signatures_uids_subkeys(key) if key.is_protected: raise CustomException("Protected/Encrypted keys are currently not supported.") key_encoded: bytes = bytes(str(key), "utf-8") self._write_file(key_file, key_encoded)
def derive_key(self, enc_params_file: str, encryption_key: str, cache_dir_path) -> str: with open(enc_params_file, "r") as file: scrypt_n = self.scrypt_n scrypt_r = self.scrypt_r scrypt_p = self.scrypt_p salt: str = "default-salt" verification_hash: Optional[str] = None for _, line in enumerate(file): if line.startswith("salt: "): salt = line.replace("salt: ", "").strip() if line.startswith("hash: "): verification_hash = line.replace("hash: ", "").strip() if line.startswith("scrypt_n: "): scrypt_n = int(line.replace("scrypt_n: ", "").strip()) if line.startswith("scrypt_r: "): scrypt_r = int(line.replace("scrypt_r: ", "").strip()) if line.startswith("scrypt_p: "): scrypt_p = int(line.replace("scrypt_p: ", "").strip()) if verification_hash is None: raise CustomException( "No hash stored in enc_params file. Can not verify encryption key." ) cached_derived_key = self._get_cached_derived_key( cache_dir_path, verification_hash) if cached_derived_key is not None and \ KeyDerivationHelper.hash_encryption_key(cached_derived_key) == verification_hash: return cached_derived_key enc_key: str = KeyDerivationHelper._derive_key(salt, scrypt_n, scrypt_r, scrypt_p, encryption_key) enc_key_hashed = KeyDerivationHelper.hash_encryption_key(enc_key) if verification_hash != enc_key_hashed: raise CustomException("Invalid encryption key provided.") self._set_cached_derived_key(cache_dir_path, verification_hash, enc_key) return enc_key
def reveal_name(self, key_id: str, encrypted: bool) -> str: """Reveals the primary name or attribute associated with the identity.""" key = self.personal_keystore.get_key(key_id) if key is None: raise CustomException("No identity with given id could be found.") revealed_uids = [] for uid in key.userids: if uid.is_primary: self.shared_keystore.add_userid(key, uid, encrypted) revealed_uids.append(uid_as_str(uid)) # this also accepts previously revealed user ids and reveals them again # (which should not create a new file) count = len(revealed_uids) if count == 0: raise CustomException( "No primary Attribute has been added for this identity. " "Please add it before attempting to reveal it.") if count >= 2: for uid in revealed_uids: logging.warning("Revealed: %s", uid) raise RuntimeWarning( "We just revealed more than one primary identity.") return revealed_uids[0]
def _cert_sign_single(key: PGPKey, signature_key: PGPKey, accepted_uids: List[str], accept_any_uid: bool) -> Optional[PGPKey]: key = copy(key) if len(key.userids) > 1: logging.error(key) raise CustomException( "Key {} contained more than one UID. Not signing any of them.". format(key.fingerprint)) key_uid = key.userids[0] if not key.verify(key_uid): raise CustomException( "Not signing UID '{}' on key {} due to missing self-signature." .format(key_uid, key.fingerprint)) # The key might contain illegal components but these will simply be passed through for now. if uid_is_in(key_uid, accepted_uids) or accept_any_uid: if may_certify(key_uid, signature_key): certification = patched_pgpy_certify( signature_key, key_uid, created=get_uniform_time_as_datetime()) key_uid |= certification return key return None
def throw() -> None: raise CustomException("Test Exception", "Second")
def cert_request(uid: str, keyid: str, output_file: TextIOWrapper) -> None: key = API.cert_request(keyid, uid) if key is None: raise CustomException("No key with this key_id and uid found.") output_file.write(str(key))