def _read_message(self, message): if not isinstance(message, Message): raise TypeError("expected a protonmail.models.Message instance") # If the message hasn't been read yet, do that now if not message.Body: resp = yield self.api.messages(message.ID, blocking=False, response=responses.MessageResponse) if resp.Code != 1000: raise ValueError("Unexpected response: {}".format( resp.to_json())) message = resp.Message # Read and decrypt if needed msg = PGPMessage.from_blob(message.Body) if msg.is_signed: email = message.SenderAddress if email not in self.PublicKeys: yield self._get_public_key(email) pk = self.PublicKeys.get(email) if not pk: raise SecurityError("Failed to verify signed message!") pk[0].verify(msg) # TODO: Support mutiple keys # Decrypt with self.PrivateKey.unlock(self.MailboxPassword) as key: message.decrypt(key) return_value(message)
def test_spurious_dash_escapes(): from pgpy import PGPKey, PGPMessage message_data = r'''-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1,SHA256 - This is stored, literally\! -----BEGIN PGP SIGNATURE----- Version: GnuPG/MacGPG2 v2.0.20 (Darwin) iJwEAQECAAYFAlQaCpEACgkQBM3VPIdAqKYrhwQAyQhwiqrR6oZ5fTBm4JyCOEND 72Kxbaz1i9Qh0jv7DmgRjb4udh95UQ8U0qVnmnhA8E2deKeDcWTS4fzUkU6J9OdH /GPHpL9QEtOJ7xifzJsnKaNJVynmNMtYOqHQ9gCmXx7jM2ngxbTKBT8YZlSLMUdO uoUFKrJGv0LWlSWHkeOJARwEAQECAAYFAlQaCpEACgkQKoNNjlkY6IYrhwf/ZnMN yKIVxGl+5/9oovvgz2MtGt9B09xRg4BqD+lUDshzQUvQIjBXZ7ZEGSWqerRymZDg ZzHpb1lv9oAOVU8f1qsMQJJkiz7Q+xu5FfgAp0WzMHJNy4QOmB4Kw/7UbTwdUXzw EzKwbJ8Eg97vJgYdfqUZLu949dwJvyYZzGDdkbrnsaZ8H29XkKXNMlMinDQjvFBR djgkILl3ZIdC3p+KechV3uYsqwje2qNEo69KukihPhzCe9o6/Yub5gdC+DSQDGl4 uPjk0zXjds4G5J5Jd5g4o7vhDWs8InxX4AcLfD6lH1XQ1VCZBpucun5CVsU3dUAv yvO7C7FubDu1GUxdbYheBAERCAAGBQJUGgqRAAoJEKXc3JZkUxQOZ+IA/3KI8Mnl k3jfpRQcvtSYFlU9WZk9SqZX6xirnV7Hloq6AP9ZlivPrJdWmjRyyShkMNgP/c63 cjMX82ahGPUVlyMP4A== =bcSu -----END PGP SIGNATURE----- ''' key = PGPKey.from_file('tests/testdata/keys/rsa.1.pub.asc')[0] message = PGPMessage.from_blob(message_data) assert key.verify(message)
def __init__(self, config, secrets): try: dataKeyBytes = config.get("key", None) except AttributeError as e: raise InvalidConfig(original=e) if not dataKeyBytes or dataKeyBytes == "": msg = ("crypto.key is not a string, " "Remove the crypto property if encryption is not needed") raise InvalidConfig(message=msg) privateKey = secrets.get("privateKey", None) if not privateKey or privateKey == "": msg = ("No gpg private key provided for decryption. " "Remove the crypto property if encryption is not needed") raise InvalidConfig(message=msg) gpgKey = PGPKey() gpgKey.parse(privateKey) password = secrets.get("privateKeyPassword", None) if password: try: gpgKey.unlock(password) except PGPDecryptionError as err: raise BadGPGKeyPasswordError(gpgKey.userids[0]) with warnings.catch_warnings(): # prevents warning of type `UserWarning: Message was encrypted with this key's subkey: ...` warnings.simplefilter("ignore", category=UserWarning) dataKey = gpgKey.decrypt( PGPMessage.from_blob(dataKeyBytes)).message DataKeyService.__init__(self, dataKey)
def decrypt_session_key(blob, key=None, password=None): """ Decrypt the session key from the given PGPMessage blob Parameters ---------- blob: String or Bytes PGP Message data key: pgpy.PGPKey The private PGPKey for decryption. It MUST be unlocked. password: String or Bytes The password for decryption Returns ------- result: Tuple[pgpy.constants.SymmetricKeyAlgorithm, Bytes] The algo and session key """ message = PGPMessage.from_blob(blob) for sk in message._sessionkeys: k = key.subkeys.get(sk.encrypter) if k is not None: cipher, session_key = sk.decrypt_sk(k._key) return cipher, bytes(session_key) raise KeyError("Key not found")
def check_mailbox_password(key, password, access_token): """ Make sure the password is valid by decrypting the access token. Parameters ---------- key: Bytes PGP Private key password: String access_token: String Access token as an encrypted PGP message Returns ------- token: String """ if not key: raise ValueError("Missing private key") if not password: raise ValueError("Missing password") if not access_token: raise ValueError("Missing access token") msg = PGPMessage.from_blob(access_token) pk, _ = PGPKey.from_blob(key) if IS_PY3 and isinstance(password, bytes): password = password.decode() with pk.unlock(password) as uk: return bytes(uk.decrypt(msg).message)
def get_package_keycode(self, package_id, public_key_id=None, private_key=None): """ Gets the decrypted package keycode using trusted device keys. Trusted device must have been assigned prior to the package being uploaded. :param package_id: The package Id :param public_key_id: The public key id for the trusted device :param private_key: The private trusted device key :return: """ #if path_to_keys: # data = read_key_pair(path_to_keys) # public_key_id = data["publicKeyId"] # private_key = data["privateKey"] if public_key_id is None or private_key is None: public_key_id = self.KEY_ID private_key = self.KEY_PAIR endpoint = '/package/' + package_id + '/link/' + public_key_id url = self.BASE_URL + endpoint headers = make_headers(self.API_SECRET, self.API_KEY, endpoint) try: keycode = requests.get(url, headers=headers).json()["message"] key_pair = pgpy.PGPKey.from_blob(str(private_key))[0] keycode_message = PGPMessage.from_blob(keycode) decrypted_keycode = key_pair.decrypt(keycode_message).message return {"keyCode": decrypted_keycode} except Exception as e: raise GetKeycodeFailedException(details=str(e))
def download_and_decrypt_file(self, file_id, directory_id=None, download_directory=".", file_name=None, progress_instance=None): """ Downloads & decrypts the specified file to the path specified """ self._block_operation_without_keycode() file_info = self.get_file_information(file_id) if not file_name: file_name = file_info["fileName"] total = file_info["fileParts"] file_path = download_directory + "/" + file_name passphrase = self.server_secret + self.client_secret progress = 1 start, end = 1, 25 try: with open(file_path, "wb") as file: while start <= total: parts = _get_download_urls(self, file_id, directory_id, start=start, end=end) for part in parts: response = bytes(requests.get(url=part["url"]).content) ba = bytearray() ba.extend(response) message = PGPMessage.from_blob(ba) decrypted = message.decrypt(passphrase=passphrase).message if isinstance(decrypted, str): decrypted = bytes(decrypted, "utf-8") file.write(decrypted) self.calculate_progress(file_id, progress, total, progress_instance) progress = progress + 1 start, end = start + 25, end + 25 except Exception as e: raise DownloadFileException(details=str(e))
def test_decrypt_unsigned_message(): from pgpy import PGPKey, PGPMessage from pgpy.errors import PGPError # these keys are small because the regression test doesn't really need the security # if you're reading this, *DO NOT GENERATE RSA KEYS THIS SMALL* # also, it's probably better to sign-then-encrypt rather than encrypt-then-sign decrypt_key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" \ "Version: PGPy v0.4.2\n" \ "\n" \ "xcA4BFlKzk4BAgDL9E6Lpzq9yNhRP49HXeOSYTz4DPI1A2wxwI97qjZFsJ2lJ2aV\n" \ "SYFpbuS6DEPaya+98HQ6xM7o2PhbUnHqcXHzABEBAAEAAf9U/XOVwpQ57e4mvWPJ\n" \ "i5h/sUGk5FAyQ0Dc4q9oCyAenaIIe5npbsR+oKmUHwJ5wWgfrTaxvAkBl15kMtSN\n" \ "VItBAQDv/8BdIdW2Bc9+qvCtC2xiUJ/3Rd+eyXMZhn4VMdA8sQEA2Y1aRBpWjHo9\n" \ "g9KydxAewt8LUwchRHeonMmILuZ58eMBALP8euss11ELnjDOLrgRP2swnOTTTk3b\n" \ "P6aV8/rbcEXOUgPNG1JlZ3Jlc3NvIEVuY3J5cHRlciAoUFIjMTgzKcJrBBMBAgAV\n" \ "BQJZSs6CAhsOAgsHAhUCAhYAAh4BAAoJEA2I8KkOVzh/+IMCAI308quFk/lJXPF/\n" \ "bpvwwgFa9bRdIzl07Qu+3oQcEm+1cu6ivznewIEmQclSUpSLjXrS/LysQSAQye+J\n" \ "PgSEalQ=\n" \ "=Sg/Y\n" \ "-----END PGP PRIVATE KEY BLOCK-----\n" sign_key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" \ "Version: PGPy v0.4.2\n" \ "\n" \ "xcA4BFlKzkMBAgDQZA3bao1qo3XkuUDOaFm1x5TkAAMUUUxtmj+dSR0wl7uRzxWm\n" \ "8naFpsJ1Mah/I8RlS1oZizaDI7BzbOvGUGjLABEBAAEAAf95RBAQQ/QhPxfmzqrY\n" \ "sj6qGocZGqywERMxoJYuOBLFaCjdT8xk0syI0LOCetwDmUWerUPWO52w9T5Gj295\n" \ "YUDpAQD7DSmifDMssvG5F9JYWdKobEwWxVsjyaYR/vbH/1Iy3QEA1H+e66Jz1ERl\n" \ "yPLyl4E5chwO2l+VMxiFod3Dvo8C68cA/0GWJIdK0NzSNZwS6wFabZg2R1pZWxJJ\n" \ "B0tsI0EqbUgNTiXNGFJlZ3Jlc3NvIFNpZ25lciAoUFIjMTgzKcJoBBMBAgASBQJZ\n" \ "Ss53AhsCAhUCAhYAAh4BAAoJED6S3OqHJjksTzQCAM73UuXFtM2qXp4zfOGYEMsj\n" \ "gcKFuFFLyNOhPZo6REeJC7o2+9d7Mwys8wVNTuS3D3o1h49QpYYNjYlgNSZ85pU=\n" \ "=DBkI\n" \ "-----END PGP PRIVATE KEY BLOCK-----\n" msg = "-----BEGIN PGP MESSAGE-----\n" \ "Version: PGPy v0.4.2\n" \ "\n" \ "xA0DAAIBPpLc6ocmOSwAwUwDDYjwqQ5XOH8BAfwOTH6C/lk5bQevArYnrf0q3Dde\n" \ "JDjM/otBckiTS8kvFz1XFfQhIDkZl+fDcRwDFNe9+JKLqOM4jU6FIUwToYgz0ksB\n" \ "f6iZ80U0dzHGtvmEzYSnsYWAglik0ch/E9tyNq/lryrLnrxWu7V26wPfI1TISuKd\n" \ "U+w1HPGoH8ugo6GkeqBdeED6gJfKEm1qgrHCXAQAAQIABgUCWUrVMQAKCRA+ktzq\n" \ "hyY5LLcHAgDHYjKVbpd5/FV4+CZ0H5yTnrD/vZ+QebDC7CmOM7f1Q5L1AdG/K1rr\n" \ "+Ud/YHq3NVk5UGU0LDfjdBwVaJmOjEUx\n" \ "=ITfp\n" \ "-----END PGP MESSAGE-----\n" dkey, _ = PGPKey.from_blob(decrypt_key) skey, _ = PGPKey.from_blob(sign_key) encmsg = PGPMessage.from_blob(msg) # this should work decmsg = dkey.decrypt(encmsg) assert decmsg.message == "Regression Test for PR#183" # this should raise PGPError, not PGPDecryptionError with pytest.raises(PGPError): skey.decrypt(encmsg)
def test_decrypt_unsigned_message(): from pgpy import PGPKey, PGPMessage from pgpy.errors import PGPError # these keys are small because the regression test doesn't really need the security # if you're reading this, *DO NOT GENERATE RSA KEYS THIS SMALL* # also, it's probably better to sign-then-encrypt rather than encrypt-then-sign decrypt_key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" \ "Version: PGPy v0.4.2\n" \ "\n" \ "xcA4BFlKzk4BAgDL9E6Lpzq9yNhRP49HXeOSYTz4DPI1A2wxwI97qjZFsJ2lJ2aV\n" \ "SYFpbuS6DEPaya+98HQ6xM7o2PhbUnHqcXHzABEBAAEAAf9U/XOVwpQ57e4mvWPJ\n" \ "i5h/sUGk5FAyQ0Dc4q9oCyAenaIIe5npbsR+oKmUHwJ5wWgfrTaxvAkBl15kMtSN\n" \ "VItBAQDv/8BdIdW2Bc9+qvCtC2xiUJ/3Rd+eyXMZhn4VMdA8sQEA2Y1aRBpWjHo9\n" \ "g9KydxAewt8LUwchRHeonMmILuZ58eMBALP8euss11ELnjDOLrgRP2swnOTTTk3b\n" \ "P6aV8/rbcEXOUgPNG1JlZ3Jlc3NvIEVuY3J5cHRlciAoUFIjMTgzKcJrBBMBAgAV\n" \ "BQJZSs6CAhsOAgsHAhUCAhYAAh4BAAoJEA2I8KkOVzh/+IMCAI308quFk/lJXPF/\n" \ "bpvwwgFa9bRdIzl07Qu+3oQcEm+1cu6ivznewIEmQclSUpSLjXrS/LysQSAQye+J\n" \ "PgSEalQ=\n" \ "=Sg/Y\n" \ "-----END PGP PRIVATE KEY BLOCK-----\n" sign_key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" \ "Version: PGPy v0.4.2\n" \ "\n" \ "xcA4BFlKzkMBAgDQZA3bao1qo3XkuUDOaFm1x5TkAAMUUUxtmj+dSR0wl7uRzxWm\n" \ "8naFpsJ1Mah/I8RlS1oZizaDI7BzbOvGUGjLABEBAAEAAf95RBAQQ/QhPxfmzqrY\n" \ "sj6qGocZGqywERMxoJYuOBLFaCjdT8xk0syI0LOCetwDmUWerUPWO52w9T5Gj295\n" \ "YUDpAQD7DSmifDMssvG5F9JYWdKobEwWxVsjyaYR/vbH/1Iy3QEA1H+e66Jz1ERl\n" \ "yPLyl4E5chwO2l+VMxiFod3Dvo8C68cA/0GWJIdK0NzSNZwS6wFabZg2R1pZWxJJ\n" \ "B0tsI0EqbUgNTiXNGFJlZ3Jlc3NvIFNpZ25lciAoUFIjMTgzKcJoBBMBAgASBQJZ\n" \ "Ss53AhsCAhUCAhYAAh4BAAoJED6S3OqHJjksTzQCAM73UuXFtM2qXp4zfOGYEMsj\n" \ "gcKFuFFLyNOhPZo6REeJC7o2+9d7Mwys8wVNTuS3D3o1h49QpYYNjYlgNSZ85pU=\n" \ "=DBkI\n" \ "-----END PGP PRIVATE KEY BLOCK-----\n" msg = "-----BEGIN PGP MESSAGE-----\n" \ "Version: PGPy v0.4.2\n" \ "\n" \ "xA0DAAIBPpLc6ocmOSwAwUwDDYjwqQ5XOH8BAfwOTH6C/lk5bQevArYnrf0q3Dde\n" \ "JDjM/otBckiTS8kvFz1XFfQhIDkZl+fDcRwDFNe9+JKLqOM4jU6FIUwToYgz0ksB\n" \ "f6iZ80U0dzHGtvmEzYSnsYWAglik0ch/E9tyNq/lryrLnrxWu7V26wPfI1TISuKd\n" \ "U+w1HPGoH8ugo6GkeqBdeED6gJfKEm1qgrHCXAQAAQIABgUCWUrVMQAKCRA+ktzq\n" \ "hyY5LLcHAgDHYjKVbpd5/FV4+CZ0H5yTnrD/vZ+QebDC7CmOM7f1Q5L1AdG/K1rr\n" \ "+Ud/YHq3NVk5UGU0LDfjdBwVaJmOjEUx\n" \ "=ITfp\n" \ "-----END PGP MESSAGE-----\n" dkey, _ = PGPKey.from_blob(decrypt_key) skey, _ = PGPKey.from_blob(sign_key) encmsg = PGPMessage.from_blob(msg) # this should work decmsg = dkey.decrypt(encmsg) assert decmsg.message == "Regression Test for PR#183" # this should raise PGPError, not PGPDecryptionError with pytest.raises(PGPError): skey.decrypt(encmsg)
def decrypt(self, enc_data): if isinstance(enc_data, str): enc_msg = PGPMessage.from_blob(enc_data) else: enc_msg = enc_data keyhandle = enc_msg.issuers.pop() skey = self._get_key_from_keyhandle(keyhandle) out = skey.decrypt(enc_msg) keyinfos = [] keyinfos.append( KeyInfo(skey.key_algorithm.name, skey.key_size, skey.fingerprint.keyid, skey.userids[0].name, skey.created)) return six.b(out.message), keyinfos
def _decrypt_message(message_to_decrypt, server_secret, client_secret): """ Decrypts a message :param message_to_decrypt: The string you'd like decrypted. :param server_secret: The server secret, obtained by inspecting a package :param client_secret: The client_secret (a.k.a. keycode) used to ensure only the holders of the link are able to decrypt. :return: The decrypted message. """ passphrase = server_secret + client_secret message_bytes = base64.b64decode(bytes(message_to_decrypt, 'utf-8')) pgpmessage = PGPMessage.from_blob(message_bytes) decrypted = pgpmessage.decrypt(passphrase=passphrase).message return decrypted
def decrypt(profile, cdata, seckey=None): cmsg = cdata if isinstance(cdata, PGPMessage) \ else PGPMessage.from_blob(cdata) assert cmsg.is_encrypted if seckey is None: encrypter = cmsg.encrypters.pop() logger.debug('encrypted by %s', encrypter) addr = _get_addr_from_keyhandle(profile, encrypter) seckey = _get_seckey_from_addr(profile, addr) if seckey is None: logger.error('No secret key found to decrypt the message.') return None out = seckey.decrypt(cmsg) return out.message
def decrypt(self, key): """ Decrypts the Body and sets the DecryptedBody of this message. Parameters ---------- key: pgpy.PGPKey An unlocked PGPKey Returns ------- msg: String The decrypted message """ msg = PGPMessage.from_blob(self.Body) if msg.is_encrypted: self.DecryptedBody = key.decrypt(msg).message else: self.DecryptedBody = self.Body return self.DecryptedBody
def _verify_keybase_signature(raw_pgp_sig_msg): # load the raw pgp message pgp_msg = PGPMessage.from_blob(raw_pgp_sig_msg) # load keybase's claimed public key # see: https://keybase.io/docs/server_security/our_merkle_key kb_public_key, _ = PGPKey.from_blob(KEYBASE_PGP_VERIFICATION_KEY) # verify it: https://pgpy.readthedocs.io/en/latest/examples.html#verifying-things f = io.StringIO() with redirect_stderr(f): # suppress unnecessary stdout verification_result = kb_public_key.verify(pgp_msg) if not verification_result: raise VerificationError( "API response did not verify with Keybase's public key") good_signatures = list(verification_result.good_signatures) if len(good_signatures) != 1: logger.error(f"good_signatures = {good_signatures}") raise VerificationError( f"Expected 1 valid signature, got {len(good_signatures)} from {specific_url}" ) return json.loads(good_signatures[0].subject)
def sym_decrypt(text, passphrase): if isinstance(text, str): text = PGPMessage.from_blob(text) pmsg = text.decrypt(passphrase) return pmsg
def is_encrypted(self): if not self.Body: return False return PGPMessage.from_blob(self.Body).is_encrypted
def _decrypt(encrypted_data: str, key: PGPKey): encrypted_msg = PGPMessage.from_blob(encrypted_data) msg = key.decrypt(encrypted_msg) return str(msg.message)
ciphers=[ SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128 ], compression=[ CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed ]) # password protect private key key.protect("Password", SymmetricKeyAlgorithm.AES256, HashAlgorithm.SHA256) # print private and public key private_key = str(key) public_key = str(key.pubkey) print(private_key) print(public_key) # encrypt message using public key message = pgpy.PGPMessage.new(" This is the message cleartext") encryptedmessage = str(PGPKey.from_blob(public_key)[0].encrypt(message)) print(encryptedmessage) ## decrypt using private key private_key = PGPKey.from_blob(private_key)[0] # reads in from string format with private_key.unlock('Password') as unlocked_private_key: result = unlocked_private_key.decrypt( PGPMessage.from_blob(encryptedmessage)) ##get cleartext to a variable plaintext = result.message print(plaintext)