""" Java javax.crypto APIs implemented in Python. javax.crypto package - Cipher, KeyGenerator, Mac, KeyAgreement, SecretKey, etc. Wraps Python cryptography library. """ from __future__ import annotations from .._core import Stub, AndroidOnlyError from typing import List, Optional, Any # Optional cryptography dependency try: from cryptography.fernet import Fernet as _Fernet from cryptography.hazmat.primitives.ciphers import Cipher as _Cipher, algorithms as _algorithms, modes as _modes from cryptography.hazmat.primitives import hashes as _hashes, hmac as _hmac, padding as _padding from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC as _PBKDF2HMAC HAS_CRYPTOGRAPHY = True except ImportError: HAS_CRYPTOGRAPHY = False __all__ = [ "Cipher", "KeyGenerator", "SecretKey", "SecretKeySpec", "Mac", "KeyAgreement", "ExemptionMechanism", "EncryptedPrivateKeyInfo", "SealedObject", "NullCipher", "PBEParameterSpec", "IvParameterSpec", "OAEPParameterSpec", "PSource", "MGF1ParameterSpec", "DESedeKeySpec", "DESKeySpec", "SecretKeyFactory", "CipherInputStream", "CipherOutputStream", "BadPaddingException", "IllegalBlockSizeException", "NoSuchPaddingException", "ShortBufferException", ] # ============================================================ # Exceptions # ============================================================ class BadPaddingException(Exception, Stub): def __init__(self, message: str = None): super().__init__(message or "Bad padding") self._message = message def toString(self) -> str: return f"BadPaddingException: {self._message}" if self._message else "BadPaddingException" def equals(self, other) -> bool: return isinstance(other, BadPaddingException) and self._message == other._message def hashCode(self) -> int: return hash(self._message) & 0x7FFFFFFF class IllegalBlockSizeException(Exception, Stub): def __init__(self, message: str = None): super().__init__(message or "Illegal block size") self._message = message def toString(self) -> str: return f"IllegalBlockSizeException: {self._message}" if self._message else "IllegalBlockSizeException" def equals(self, other) -> bool: return isinstance(other, IllegalBlockSizeException) and self._message == other._message def hashCode(self) -> int: return hash(self._message) & 0x7FFFFFFF class NoSuchPaddingException(Exception, Stub): def __init__(self, message: str = None): super().__init__(message or "No such padding") self._message = message def toString(self) -> str: return f"NoSuchPaddingException: {self._message}" if self._message else "NoSuchPaddingException" def equals(self, other) -> bool: return isinstance(other, NoSuchPaddingException) and self._message == other._message def hashCode(self) -> int: return hash(self._message) & 0x7FFFFFFF class ShortBufferException(Exception, Stub): def __init__(self, message: str = None): super().__init__(message or "Output buffer too short") self._message = message def toString(self) -> str: return f"ShortBufferException: {self._message}" if self._message else "ShortBufferException" def equals(self, other) -> bool: return isinstance(other, ShortBufferException) and self._message == other._message def hashCode(self) -> int: return hash(self._message) & 0x7FFFFFFF # ============================================================ # SecretKey # ============================================================ class SecretKey(Stub): """ javax.crypto.SecretKey A secret (symmetric) key. """ serialVersionUID: int = -4795878709 def __init__(self, encoded: bytes = None, algorithm: str = None): super().__init__() self._encoded = encoded or b'' self._algorithm = algorithm or "RAW" def getAlgorithm(self) -> str: return self._algorithm def getFormat(self) -> str: return "RAW" def getEncoded(self) -> bytes: return self._encoded def toString(self) -> str: return f"SecretKey@{hex(id(self))}[algorithm={self._algorithm}]" def equals(self, other) -> bool: if isinstance(other, SecretKey): return self._encoded == other._encoded and self._algorithm == other._algorithm return False def hashCode(self) -> int: return hash((bytes(self._encoded), self._algorithm)) & 0x7FFFFFFF # ============================================================ # SecretKeySpec # ============================================================ class SecretKeySpec(SecretKey): """ javax.crypto.spec.SecretKeySpec A secret key specification. """ def __init__(self, key: bytes, algorithm: str): super().__init__(key, algorithm) self._key = key def getAlgorithm(self) -> str: return self._algorithm def getFormat(self) -> str: return "RAW" def getEncoded(self) -> bytes: return self._key def toString(self) -> str: return f"SecretKeySpec@{hex(id(self))}[algorithm={self._algorithm}]" def equals(self, other) -> bool: if isinstance(other, SecretKeySpec): return self._key == other._key and self._algorithm == other._algorithm return False def hashCode(self) -> int: return hash((bytes(self._key), self._algorithm)) & 0x7FFFFFFF # ============================================================ # Cipher # ============================================================ class Cipher(Stub): """ javax.crypto.Cipher Engine for encryption/decryption. """ ENCRYPT_MODE: int = 1 DECRYPT_MODE: int = 2 WRAP_MODE: int = 3 UNWRAP_MODE: int = 4 PUBLIC_KEY: int = 1 PRIVATE_KEY: int = 2 SECRET_KEY: int = 3 def __init__(self, transformation: str = None): super().__init__() self._transformation = transformation self._opmode = None self._key = None self._iv = None self._provider = None @staticmethod def getInstance(transformation: str) -> "Cipher": return Cipher(transformation) def init(self, opmode: int, key: Any, params: Any = None, random: Any = None): """Initializes this cipher with a key and optional parameters.""" self._opmode = opmode self._key = key if params and hasattr(params, 'getIV'): self._iv = params.getIV() def doFinal(self, input_data: bytes = None) -> bytes: """Performs the final encryption/decryption operation.""" if input_data is None: return b'' if not HAS_CRYPTOGRAPHY: # Fallback simple XOR for demonstration if self._key and hasattr(self._key, 'getEncoded'): key_bytes = self._key.getEncoded() if key_bytes: result = bytearray() for i, b in enumerate(input_data): result.append(b ^ key_bytes[i % len(key_bytes)]) return bytes(result) return input_data # Try using cryptography library try: if self._key and hasattr(self._key, 'getEncoded'): key_bytes = self._key.getEncoded() if self._transformation and "AES" in self._transformation.upper(): if len(key_bytes) in (16, 24, 32): if self._opmode == Cipher.ENCRYPT_MODE: from cryptography.hazmat.primitives.ciphers import Cipher as _CryptCipher, algorithms as _alg, modes as _mod iv = self._iv or b'\x00' * 16 c = _CryptCipher(_alg.AES(key_bytes), _mod.CBC(iv)) encryptor = c.encryptor() padder = _padding.PKCS7(128).padder() padded = padder.update(input_data) + padder.finalize() return encryptor.update(padded) + encryptor.finalize() elif self._opmode == Cipher.DECRYPT_MODE: iv = self._iv or b'\x00' * 16 c = _CryptCipher(_alg.AES(key_bytes), _mod.CBC(iv)) decryptor = c.decryptor() padded = decryptor.update(input_data) + decryptor.finalize() unpadder = _padding.PKCS7(128).unpadder() return unpadder.update(padded) + unpadder.finalize() except Exception: pass return input_data def update(self, input_data: bytes) -> bytes: """Continues a multi-part encryption/decryption operation.""" return input_data def wrap(self, key: Any) -> bytes: """Wraps a key (stub).""" if isinstance(key, SecretKey): return key.getEncoded() return b'' def unwrap(self, wrapped_key: bytes, wrapped_key_algorithm: str, wrapped_key_type: int) -> Any: """Unwraps a key (stub).""" return SecretKey(wrapped_key, wrapped_key_algorithm) def getBlockSize(self) -> int: """Returns the block size (bytes).""" if self._transformation and "AES" in self._transformation.upper(): return 16 if self._transformation and "DES" in self._transformation.upper(): return 8 return 0 def getIV(self) -> Optional[bytes]: return self._iv def getParameters(self): return None def getAlgorithm(self) -> str: return self._transformation or "NONE" def getProvider(self): return self._provider def getExemptionMechanism(self): return None def getOutputSize(self, input_len: int) -> int: """Returns the output buffer size for a given input length.""" bs = self.getBlockSize() if bs > 0: return input_len + (bs - input_len % bs) return input_len @staticmethod def getMaxAllowedKeyLength(transformation: str) -> int: """Returns the maximum key length for the given transformation.""" return 2147483647 # Integer.MAX_VALUE def toString(self) -> str: return f"Cipher@{hex(id(self))}[algorithm={self._transformation}]" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # NullCipher # ============================================================ class NullCipher(Cipher): """ javax.crypto.NullCipher A cipher that performs no transformation (identity). """ def __init__(self): super().__init__("NULL") def getBlockSize(self) -> int: return 1 def getOutputSize(self, input_len: int) -> int: return input_len def getIV(self) -> Optional[bytes]: return None def getParameters(self): return None def getExemptionMechanism(self): return None def update(self, input_data: bytes) -> bytes: return input_data def doFinal(self, input_data: bytes = None) -> bytes: return input_data if input_data else b'' def toString(self) -> str: return "NullCipher" # ============================================================ # KeyGenerator # ============================================================ class KeyGenerator(Stub): """ javax.crypto.KeyGenerator Key generator for symmetric algorithms. """ def __init__(self, algorithm: str = None): super().__init__() self._algorithm = algorithm or "AES" self._provider = None self._key_size = 128 @staticmethod def getInstance(algorithm: str) -> "KeyGenerator": return KeyGenerator(algorithm) def init(self, key_size_or_random: Any, random: Any = None): """Initializes the key generator.""" if isinstance(key_size_or_random, int): self._key_size = key_size_or_random elif key_size_or_random is not None and hasattr(key_size_or_random, 'nextBytes'): pass def generateKey(self) -> SecretKey: """Generates a secret key.""" import os key_bytes = os.urandom(self._key_size // 8) return SecretKey(key_bytes, self._algorithm) def getAlgorithm(self) -> str: return self._algorithm def getProvider(self): return self._provider def toString(self) -> str: return f"KeyGenerator@{hex(id(self))}[algorithm={self._algorithm}]" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # Mac # ============================================================ class Mac(Stub): """ javax.crypto.Mac Message Authentication Code engine. """ def __init__(self, algorithm: str = None): super().__init__() self._algorithm = algorithm or "HmacSHA256" self._provider = None self._key = None self._hmac = None self._initialized = False @staticmethod def getInstance(algorithm: str) -> "Mac": return Mac(algorithm) def init(self, key: Any, params: Any = None): """Initializes the MAC with a key.""" self._key = key self._initialized = True if HAS_CRYPTOGRAPHY and key and hasattr(key, 'getEncoded'): key_bytes = key.getEncoded() hash_algo = _hashes.SHA256() self._hmac = _hmac.HMAC(key_bytes, hash_algo) def update(self, input_data: bytes): """Processes the given byte array.""" if self._hmac: self._hmac.update(input_data) def doFinal(self, input_data: bytes = None) -> bytes: """Finishes the MAC operation.""" if self._hmac: if input_data: self._hmac.update(input_data) return self._hmac.finalize() if input_data: import hashlib return hashlib.sha256(input_data).digest() return b'\x00' * 32 def getMacLength(self) -> int: """Returns the output length of the MAC.""" return 32 # SHA-256 def getAlgorithm(self) -> str: return self._algorithm def getProvider(self): return self._provider def reset(self): """Resets the MAC.""" if self._key and hasattr(self._key, 'getEncoded') and HAS_CRYPTOGRAPHY: key_bytes = self._key.getEncoded() hash_algo = _hashes.SHA256() self._hmac = _hmac.HMAC(key_bytes, hash_algo) def clone(self): return Mac(self._algorithm) def toString(self) -> str: return f"Mac@{hex(id(self))}[algorithm={self._algorithm}]" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # KeyAgreement # ============================================================ class KeyAgreement(Stub): """ javax.crypto.KeyAgreement Key agreement protocol engine (e.g., Diffie-Hellman). """ def __init__(self, algorithm: str = None): super().__init__() self._algorithm = algorithm or "DH" self._provider = None @staticmethod def getInstance(algorithm: str) -> "KeyAgreement": return KeyAgreement(algorithm) def init(self, key: Any, params: Any = None): """Initializes this key agreement with a key.""" pass def doPhase(self, key: Any, last_phase: bool) -> Any: """Executes the next phase of the key agreement.""" return self def generateSecret(self, shared_secret: Any = None) -> bytes: """Generates the shared secret.""" import os return os.urandom(32) def getAlgorithm(self) -> str: return self._algorithm def getProvider(self): return self._provider def toString(self) -> str: return f"KeyAgreement@{hex(id(self))}[algorithm={self._algorithm}]" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # ExemptionMechanism # ============================================================ class ExemptionMechanism(Stub): """ javax.crypto.ExemptionMechanism Exemption mechanism for key recovery. """ def __init__(self, algorithm: str = None): super().__init__() self._algorithm = algorithm or "KeyRecovery" self._initialized = False @staticmethod def getInstance(algorithm: str) -> "ExemptionMechanism": return ExemptionMechanism(algorithm) def init(self, key: Any): self._initialized = True def genExemptionBlob(self, input_data: bytes = None) -> bytes: return b'\x00' * 32 def isCryptoAllowed(self, key: Any) -> bool: return True def getOutputSize(self, input_len: int) -> int: return input_len def getAlgorithm(self) -> str: return self._algorithm def getProvider(self): return None def getName(self) -> str: return self._algorithm def toString(self) -> str: return f"ExemptionMechanism@{hex(id(self))}[algorithm={self._algorithm}]" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # EncryptedPrivateKeyInfo # ============================================================ class EncryptedPrivateKeyInfo(Stub): """ javax.crypto.EncryptedPrivateKeyInfo Represents encrypted private key info from PKCS #8. """ def __init__(self, alg_params=None, encrypted_data: bytes = None): super().__init__() self._alg_params = alg_params self._encrypted_data = encrypted_data or b'' def getAlgParameters(self): return self._alg_params def getEncryptedData(self) -> bytes: return self._encrypted_data def getEncoded(self) -> bytes: return self._encrypted_data def getName(self) -> str: return "PKCS#8" def toString(self) -> str: return f"EncryptedPrivateKeyInfo@{hex(id(self))}" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # SealedObject # ============================================================ class SealedObject(Stub): """ javax.crypto.SealedObject Enables protection of a serializable object using encryption. """ def __init__(self, obj: Any = None, cipher: Cipher = None): super().__init__() self._encrypted_data = b'' self._algorithm = "NONE" if cipher and obj is not None: import pickle serialized = pickle.dumps(obj) self._encrypted_data = cipher.doFinal(serialized) self._algorithm = cipher.getAlgorithm() def getObject(self, cipher_or_key: Any = None) -> Any: """Unseals the object.""" import pickle try: if isinstance(cipher_or_key, Cipher): decrypted = cipher_or_key.doFinal(self._encrypted_data) else: decrypted = self._encrypted_data return pickle.loads(decrypted) except Exception: return None def getAlgorithm(self) -> str: return self._algorithm def getEncoded(self) -> bytes: return self._encrypted_data def toString(self) -> str: return f"SealedObject@{hex(id(self))}[algorithm={self._algorithm}]" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # ParameterSpec classes # ============================================================ class IvParameterSpec(Stub): """ javax.crypto.spec.IvParameterSpec Initialization vector specification. """ def __init__(self, iv: bytes): super().__init__() self._iv = iv def getIV(self) -> bytes: return self._iv def toString(self) -> str: return f"IvParameterSpec@{hex(id(self))}[iv={self._iv.hex()[:16]}...]" class PBEParameterSpec(Stub): """ javax.crypto.spec.PBEParameterSpec Password-based encryption parameter specification. """ def __init__(self, salt: bytes, iteration_count: int): super().__init__() self._salt = salt self._iteration_count = iteration_count def getSalt(self) -> bytes: return self._salt def getIterationCount(self) -> int: return self._iteration_count def toString(self) -> str: return f"PBEParameterSpec@{hex(id(self))}[iterations={self._iteration_count}]" class PSource(Stub): """ javax.crypto.spec.PSource Source of the label for OAEP padding. """ class PSpecified(Stub): DEFAULT: "PSource.PSpecified" = None def __init__(self, value: bytes = b''): super().__init__() self._value = value def getValue(self) -> bytes: return self._value def toString(self) -> str: return "PSpecified" def __init__(self, algorithm: str = None): super().__init__() self._algorithm = algorithm def getAlgorithm(self) -> str: return self._algorithm def toString(self) -> str: return f"PSource.{self._algorithm}" if self._algorithm else "PSource" def equals(self, other) -> bool: if isinstance(other, PSource): return self._algorithm == other._algorithm return False def hashCode(self) -> int: return hash(self._algorithm) & 0x7FFFFFFF PSource.PSpecified.DEFAULT = PSource.PSpecified(b'') class OAEPParameterSpec(Stub): """ javax.crypto.spec.OAEPParameterSpec OAEP padding parameter specification. """ DEFAULT: "OAEPParameterSpec" = None def __init__(self, digest_algorithm: str = "SHA-256", mgf_algorithm: str = "MGF1", mgf_parameters: Any = None, p_source: PSource = None): super().__init__() self._digest_algorithm = digest_algorithm self._mgf_algorithm = mgf_algorithm self._mgf_parameters = mgf_parameters self._p_source = p_source or PSource.PSpecified.DEFAULT def getDigestAlgorithm(self) -> str: return self._digest_algorithm def getMGFAlgorithm(self) -> str: return self._mgf_algorithm def getMGFParameters(self): return self._mgf_parameters def getPSource(self) -> PSource: return self._p_source def getPSourceAlgorithm(self) -> str: return self._p_source.getAlgorithm() if self._p_source else "" def toString(self) -> str: return f"OAEPParameterSpec@{hex(id(self))}[digest={self._digest_algorithm}]" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF OAEPParameterSpec.DEFAULT = OAEPParameterSpec() class MGF1ParameterSpec(Stub): """ javax.crypto.spec.MGF1ParameterSpec MGF1 parameter specification. """ SHA1: "MGF1ParameterSpec" = None SHA224: "MGF1ParameterSpec" = None SHA256: "MGF1ParameterSpec" = None SHA384: "MGF1ParameterSpec" = None SHA512: "MGF1ParameterSpec" = None SHA512_224: "MGF1ParameterSpec" = None SHA512_256: "MGF1ParameterSpec" = None SHA3_224: "MGF1ParameterSpec" = None SHA3_256: "MGF1ParameterSpec" = None SHA3_384: "MGF1ParameterSpec" = None SHA3_512: "MGF1ParameterSpec" = None def __init__(self, digest_algorithm: str = "SHA-1"): super().__init__() self._digest_algorithm = digest_algorithm def getDigestAlgorithm(self) -> str: return self._digest_algorithm def toString(self) -> str: return f"MGF1ParameterSpec[{self._digest_algorithm}]" def equals(self, other) -> bool: if isinstance(other, MGF1ParameterSpec): return self._digest_algorithm == other._digest_algorithm return False def hashCode(self) -> int: return hash(self._digest_algorithm) & 0x7FFFFFFF MGF1ParameterSpec.SHA1 = MGF1ParameterSpec("SHA-1") MGF1ParameterSpec.SHA224 = MGF1ParameterSpec("SHA-224") MGF1ParameterSpec.SHA256 = MGF1ParameterSpec("SHA-256") MGF1ParameterSpec.SHA384 = MGF1ParameterSpec("SHA-384") MGF1ParameterSpec.SHA512 = MGF1ParameterSpec("SHA-512") MGF1ParameterSpec.SHA512_224 = MGF1ParameterSpec("SHA-512/224") MGF1ParameterSpec.SHA512_256 = MGF1ParameterSpec("SHA-512/256") MGF1ParameterSpec.SHA3_224 = MGF1ParameterSpec("SHA3-224") MGF1ParameterSpec.SHA3_256 = MGF1ParameterSpec("SHA3-256") MGF1ParameterSpec.SHA3_384 = MGF1ParameterSpec("SHA3-384") MGF1ParameterSpec.SHA3_512 = MGF1ParameterSpec("SHA3-512") class DESedeKeySpec(Stub): """ javax.crypto.spec.DESedeKeySpec Key specification for DES-EDE (Triple DES) keys. """ DES_EDE_KEY_LEN: int = 24 def __init__(self, key: bytes): super().__init__() self._key = key def getKey(self) -> bytes: return self._key @staticmethod def isParityAdjusted(key: bytes) -> bool: return True def toString(self) -> str: return f"DESedeKeySpec@{hex(id(self))}" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF class DESKeySpec(Stub): """ javax.crypto.spec.DESKeySpec Key specification for DES keys. """ DES_KEY_LEN: int = 8 def __init__(self, key: bytes): super().__init__() self._key = key def getKey(self) -> bytes: return self._key @staticmethod def isParityAdjusted(key: bytes) -> bool: return True @staticmethod def isWeak(key: bytes) -> bool: return False def toString(self) -> str: return f"DESKeySpec@{hex(id(self))}" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # SecretKeyFactory # ============================================================ class SecretKeyFactory(Stub): """ javax.crypto.SecretKeyFactory Factory for converting between opaque and transparent keys. """ def __init__(self, algorithm: str = None): super().__init__() self._algorithm = algorithm or "AES" self._provider = None @staticmethod def getInstance(algorithm: str) -> "SecretKeyFactory": return SecretKeyFactory(algorithm) def translateKey(self, key: SecretKey) -> SecretKey: return key def generateSecret(self, key_spec: Any) -> SecretKey: if isinstance(key_spec, SecretKeySpec): return key_spec return SecretKey(b'', self._algorithm) def getKeySpec(self, key: SecretKey, key_spec_cls: type) -> Any: if key_spec_cls == SecretKeySpec: return SecretKeySpec(key.getEncoded(), key.getAlgorithm()) return key def getAlgorithm(self) -> str: return self._algorithm def getProvider(self): return self._provider def toString(self) -> str: return f"SecretKeyFactory@{hex(id(self))}[algorithm={self._algorithm}]" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # CipherInputStream # ============================================================ class CipherInputStream(Stub): """ javax.crypto.CipherInputStream A CipherInputStream is composed of an InputStream and a Cipher. """ def __init__(self, istream, cipher: Cipher): super().__init__() self._istream = istream self._cipher = cipher self._buffer = b'' def read(self, b: bytearray = None, off: int = 0, le: int = 0) -> int: data = self._istream.read(le) if le else self._istream.read() if not data: return -1 decrypted = self._cipher.update(data) self._buffer += decrypted if b is not None and le > 0: n = min(le, len(self._buffer)) b[off:off + n] = self._buffer[:n] self._buffer = self._buffer[n:] return n return len(data) def available(self) -> int: return len(self._buffer) def skip(self, n: int) -> int: self._buffer = self._buffer[n:] return n def close(self): if hasattr(self._istream, 'close'): self._istream.close() def markSupported(self) -> bool: return False def mark(self, read_limit: int): pass def reset(self): pass def toString(self) -> str: return f"CipherInputStream@{hex(id(self))}" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # CipherOutputStream # ============================================================ class CipherOutputStream(Stub): """ javax.crypto.CipherOutputStream A CipherOutputStream is composed of an OutputStream and a Cipher. """ def __init__(self, ostream, cipher: Cipher): super().__init__() self._ostream = ostream self._cipher = cipher def write(self, b: Any, off: int = None, le: int = None): if isinstance(b, int): data = bytes([b]) elif off is not None and le is not None: data = bytes(b[off:off + le]) else: data = bytes(b) if not isinstance(b, bytes) else b encrypted = self._cipher.update(data) if hasattr(self._ostream, 'write'): self._ostream.write(encrypted) def flush(self): if hasattr(self._ostream, 'flush'): self._ostream.flush() def close(self): final = self._cipher.doFinal() if final and hasattr(self._ostream, 'write'): self._ostream.write(final) if hasattr(self._ostream, 'close'): self._ostream.close() def toString(self) -> str: return f"CipherOutputStream@{hex(id(self))}" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF