def hybrid_encrypt(message: bytes, pk: rsa.RSAPublicKey) -> TapCHData: """ This method is the hybrid encrypt outlined in the Tor spec 0.4 section :param message: The message to be encrypted :param pk: The RSA public to encrypt the message with :return: The object TapCHData that has the client handshake data """ # First convert the message to a byte array so that we can slice it etc. message = bytearray(message) if len(message ) <= CryptoConstants.PK_ENC_LEN - CryptoConstants.PK_PAD_LEN: # Then encrypt the message using the onion key p = pk.encrypt( message, padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)) # Create the TAP_H_DATA object padding_bytes = bytes(CryptoConstants.PK_PAD_LEN) tap_h_data = TapCHData(padding_bytes, None, p, None) else: k = bytearray(os.urandom(CryptoConstants.KEY_LEN)) m1 = message[0:CryptoConstants.PK_ENC_LEN - CryptoConstants.PK_PAD_LEN - CryptoConstants.KEY_LEN] m2 = message[CryptoConstants.PK_ENC_LEN - CryptoConstants.PK_PAD_LEN - CryptoConstants.KEY_LEN:] p1 = pk.encrypt( bytes(k + m1), padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None)) nonce = bytes( CryptoConstants.KEY_LEN) # all bytes are 0, nonce is the IV cipher = Cipher(algorithms.AES(k), modes.CTR(nonce), backend=default_backend()) encryptor = cipher.encryptor() p2 = encryptor.update(m2) + encryptor.finalize() print(p1, len(p1)) print(p2, len(p2)) print(m2, len(m2)) # tap_h_data = TapCHData(bytearray(p1)[CryptoConstants.PK_ENC_LEN - CryptoConstants.PK_PAD_LEN:], # bytearray(p1)[0:CryptoConstants.KEY_LEN], # bytearray(p1)[CryptoConstants.KEY_LEN:(CryptoConstants.PK_ENC_LEN - CryptoConstants.PK_PAD_LEN)], # p2) tap_h_data = TapCHData( bytearray(p1)[0:CryptoConstants.PK_PAD_LEN], bytearray(p1)[CryptoConstants.PK_PAD_LEN:( CryptoConstants.PK_PAD_LEN + CryptoConstants.KEY_LEN)], bytearray(p1)[(CryptoConstants.PK_PAD_LEN + CryptoConstants.KEY_LEN):], p2) return tap_h_data
def hybrid_encrypt(message: bytes, pk: rsa.RSAPublicKey): """ Some specifications will refer to the "legacy hybrid encryption" of a byte sequence M with a public key PK. It is computed as follows: 1. If the length of M is no more than PK_ENC_LEN-PK_PAD_LEN, pad and encrypt M with PK. 2. Otherwise, generate a KEY_LEN byte random key K. Let M1 = the first PK_ENC_LEN-PK_PAD_LEN-KEY_LEN bytes of M, and let M2 = the rest of M. Pad and encrypt K|M1 with PK. Encrypt M2 with our stream cipher, using the key K. Concatenate these encrypted values. :param message: the message to encrypt :param pk: the public RSA key :return: the encrypted message """ # This if shouldn't ever be true, because this algorithm is used with diffie hellman 128 bit keys. # M will be 128 bits, and adding key and padding to it will always make it more than 128 bits if len(message ) <= CryptoConstants.PK_ENC_LEN - CryptoConstants.PK_PAD_LEN: encrpt_msg = pk.encrypt( message, padding.OAEP(mgf=padding.MGF1(hashes.SHA1()), algorithm=hashes.SHA1(), label=None)) padding_bytes = bytes(CryptoConstants.PK_PAD_LEN) tap_h_data = "temporary" print("[*] We got a shorter message. Weird") else: # Split the message m1 = message[:CryptoConstants.PK_ENC_LEN - CryptoConstants.PK_PAD_LEN - CryptoConstants.KEY_LEN] m2 = message[CryptoConstants.PK_ENC_LEN - CryptoConstants.PK_PAD_LEN - CryptoConstants.KEY_LEN:] # Generate encryption key for AES key = bytearray(os.urandom(CryptoConstants.KEY_LEN)) m1 = bytes(key + m1) # Encrypt first part with RSA encrpt_msg1 = pk.encrypt( m1, padding.OAEP(mgf=padding.MGF1(hashes.SHA1()), algorithm=hashes.SHA1(), label=None)) nonce = bytes(CryptoConstants.KEY_LEN) cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=default_backend()) # Encrypt second part with AES encryptor = cipher.encryptor() encrpt_msg2 = encryptor.update(m2) + encryptor.finalize() return encrpt_msg1 + encrpt_msg2
def encrypt(self, msg: str, public_key: RSAPublicKey) -> str: encrypted = public_key.encrypt( msg.encode("ascii"), padding=padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)) return binascii.b2a_base64(encrypted).decode("utf-8")
def pk_encrypt(message: bytes, public_key: rsa.RSAPublicKey) -> bytes: """ Encrypt a message using RSAES-OAEP. """ ciphertext = public_key.encrypt( message, padding.OAEP(padding.MGF1(hashes.SHA1()), hashes.SHA1(), None)) return ciphertext
def pk_encrypt(message: bytes, public_key: rsa.RSAPublicKey) -> bytes: """ Encrypt a message using RSAES-OAEP. """ ciphertext = public_key.encrypt(message, padding.OAEP( padding.MGF1(hashes.SHA1()), hashes.SHA1(), None)) return ciphertext
def rsa_encrypt(public_key: rsa.RSAPublicKey, message: bytes) -> bytes: return public_key.encrypt( message, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None, ), )
def _generate_rsa_seed(key: rsa.RSAPublicKey, hashAlg: int, label: bytes) -> Tuple[bytes, bytes]: halg = _get_digest(hashAlg) if halg is None: raise ValueError(f"unsupported digest algorithm {hashAlg}") seed = secrets.token_bytes(halg.digest_size) mgf = padding.MGF1(halg()) padd = padding.OAEP(mgf, halg(), label) enc_seed = key.encrypt(seed, padd) return (seed, enc_seed)
def encrypt(self, message: bytes, otherkey: rsa.RSAPublicKey): signature = self.__privkey.sign( message, apadding.PSS(mgf=apadding.MGF1(hashes.SHA256()), salt_length=apadding.PSS.MAX_LENGTH), hashes.SHA256()) ciphertext = otherkey.encrypt( message, apadding.OAEP(mgf=apadding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)) return signature + ciphertext
def rsa_encrypt(short_message: bytes, public_key: rsa.RSAPublicKey) -> bytes: """ encrypt Optimal Asymmetric Encryption Padding (OAEP)""" hash_algorithm = hashes.SHA512 if public_key.key_size >= 2048 else hashes.SHA256 return public_key.encrypt( plaintext=short_message, padding=padding.OAEP( mgf=padding.MGF1(algorithm=hash_algorithm()), algorithm=hash_algorithm(), label=None ) )
def encrypt(pub_key: rsa.RSAPublicKey, msg: str | bytes) -> bytes: """ Encrypt a chat message and return the encrypted string. :param pub_key: RSA public key to encrypt the message with. :type pub_key: :class:`rsa.RSAPublicKey` :param msg: Message to encrypt. :type msg: :type:`str` :return: Encrypted message. :rtype: bytes """ msg = _bytes(msg) return pub_key.encrypt( msg, padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
def send_pgp_key(self, msg: bytes, receiver_public_key: RSAPublicKey) -> PGPPacket: """ Send `msg` using PGP method :param msg: secret message :param receiver_public_key: receiver's public key :return: List containing encrypted message, signed hash and secret key. Message and hash also contain their IV. """ # Sign message hash signed_hash: bytes = self.sign_message(msg) # Zip message and its signed hash message: List[bytes] = [msg, signed_hash] zipped_message: List[bytes] = [zlib.compress(m) for m in message] # Encrypt message and hash as AESEncryptedData iv = os.urandom(16) encrypted_message: List = [ AESEncryptedData(data=self.encrypt(m, iv), iv=iv) for m in zipped_message ] # Encrypt secret_key using receiver's public key encrypted_secret_key: bytes = receiver_public_key.encrypt( plaintext=self._secret_key, padding=padding.OAEP(padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)) # Add encrypted secret key to message encrypted_message.append(encrypted_secret_key) packet = PGPPacket(data=encrypted_message[0], signature=encrypted_message[1], secret_key=encrypted_message[2]) return packet