def DecryptString(self, ciphertext: str) -> str: ciphertext_bytes = base64.b64decode(ciphertext) salt, header, body = ciphertext_bytes[:48], ciphertext_bytes[ 48:48 + 16], ciphertext_bytes[48 + 16:] if len(salt) == 48 and len(header) == 16: xts_key = pbkdf2.PBKDF2HMAC(hashes.SHA1(), 128 // 8 * 2, salt[0:40], 1000, default_backend()).derive( self._password) else: raise ValueError('Broken ciphertext: length is too short.') xts_cipher = Cipher( algorithms.AES(xts_key), modes.XTS( b'\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00' ), default_backend()) xts_decryptor = xts_cipher.decryptor() header = xts_decryptor.update(header) + xts_decryptor.finalize() header_tag, header_textlength, header_blocksize = \ header[0:4], int.from_bytes(header[4:12], 'little'), int.from_bytes(header[12:16], 'little') padded_textlength = header_textlength if header_textlength % 16 == 0 else header_textlength + 16 - header_textlength % 16 if not (header_tag == b'XTS1'): raise ValueError('Broken ciphertext: header_tag is corrupted.') if not (padded_textlength == len(body)): raise ValueError( 'Broken ciphertext: header_textlength is corrupted.') if not (0 < header_blocksize <= 0x100000 and header_blocksize % 16 == 0): raise ValueError( 'Broken ciphertext: header_blocksize is corrupted.') plaintext_bytes = bytearray() for i in itertools.count(): offset_begin = i * header_blocksize offset_end = offset_begin + header_blocksize if offset_begin < len(body): xts_cipher = Cipher(algorithms.AES(xts_key), modes.XTS(i.to_bytes(16, 'little')), default_backend()) xts_decryptor = xts_cipher.decryptor() block = xts_decryptor.update( body[offset_begin:offset_end]) + xts_decryptor.finalize() if len( block ) < header_blocksize and header_textlength != padded_textlength: block = block[0:header_textlength - padded_textlength] plaintext_bytes.extend(block) else: break return plaintext_bytes.decode('utf-8')
def decrypt_keybag(self, wrapped_keybag, offset, key): """ Decrypts the wrapped binary keybag that will be decrypted using the Container's UUID. Uses the setKey function as the cipher The UUID is set as both the first and second key in the cipher param: wrapped_keybag: The wrapped binary keybag that will be decrypted using the Container's UUID """ cs_factor = self.block_size // 0x200 uno = offset * cs_factor complete_plaintext = b"" # Cipher is AES-XTS with the container UUID as the first and second key try: log.debug("Attempting to decrypt the keybag") k = 0 size = len(wrapped_keybag) while k < size: tweak = struct.pack("<QQ", uno, 0) decryptor = Cipher(algorithms.AES(key + key), modes.XTS(tweak), backend=default_backend()).decryptor() complete_plaintext += decryptor.update( wrapped_keybag[k:k + 0x200]) + decryptor.finalize() uno += 1 k += 0x200 log.debug("Successfully decrypted the keybag") return complete_plaintext except InternalError as ex: log.exception("Could not decrypt the keybag.") return ''
def read(self, path, size, offset, fh): obfs_path = self.path_obfuscate(path) if path == self.proc_wl_path: return str(self.proc_wl).encode() start_block = offset // self.block_size num_blocks = size // self.block_size if (size % self.block_size): num_blocks += 1 rb_data = self.read_block(path, start_block, num_blocks, fh) # decryption here # pt = rb_data pt = b"" rb_data_len = len(rb_data) i = 0 while len(pt) < rb_data_len: ct = rb_data[:self.block_size] tweak1, tweak2 = start_block + i * self.block_size // 256, start_block + i * self.block_size % 256 decryptor = Cipher(algorithms.AES(self.key), modes.XTS(struct.pack('>QQ', tweak1, tweak2)), backend=default_backend()).decryptor() pt += decryptor.update(ct) rb_data = rb_data[self.block_size:] i += 1 start = offset % self.block_size return pt[start:(start + size)]
def test_xts_too_short(self, backend): key = b"thirty_two_byte_keys_are_great!!" tweak = b"\x00" * 16 cipher = base.Cipher(algorithms.AES(key), modes.XTS(tweak)) enc = cipher.encryptor() with pytest.raises(ValueError): enc.update(b"0" * 15)
def test_xts_vectors(self, backend, subtests): # This list comprehension excludes any vector that does not have a # data unit length that is divisible by 8. The NIST vectors include # tests for implementations that support encryption of data that is # not divisible modulo 8, but OpenSSL is not such an implementation. vectors = [ x for x in _load_all_params( os.path.join("ciphers", "AES", "XTS", "tweak-128hexstr"), ["XTSGenAES128.rsp", "XTSGenAES256.rsp"], load_nist_vectors, ) if int(x["dataunitlen"]) / 8.0 == int(x["dataunitlen"]) // 8 ] for vector in vectors: with subtests.test(): key = binascii.unhexlify(vector["key"]) tweak = binascii.unhexlify(vector["i"]) pt = binascii.unhexlify(vector["pt"]) ct = binascii.unhexlify(vector["ct"]) cipher = base.Cipher(algorithms.AES(key), modes.XTS(tweak), backend) enc = cipher.encryptor() computed_ct = enc.update(pt) + enc.finalize() assert computed_ct == ct dec = cipher.decryptor() computed_pt = dec.update(ct) + dec.finalize() assert computed_pt == pt
def _e(x, seed): # not sure seed is the right term here... x = x.encode("utf8") + bytes([0] * (16 - len(x))) b = hashlib.sha512(seed.encode("utf8") + ENCRYPT_KEY + SALT).digest()[-16:] c = Cipher(algorithms.AES(XTS_KEY), modes.XTS(b), backend=default_backend()) e = c.encryptor() return base64.urlsafe_b64encode(e.update(x) + e.finalize()).decode("utf8")
def _d(x, seed): # not sure seed is the right term here... x = base64.urlsafe_b64decode(x) b = hashlib.sha512(seed.encode("utf8") + ENCRYPT_KEY + SALT).digest()[-16:] c = Cipher(algorithms.AES(XTS_KEY), modes.XTS(b), backend=default_backend()) d = c.decryptor() return (d.update(x) + d.finalize()).rstrip(b"\x00").decode("utf8")
def EncryptString(self, plaintext: str) -> str: plaintext_bytes = plaintext.encode('utf-8') salt = os.urandom(48) header_tag = b'XTS1' header_textlength = len(plaintext_bytes) header_blocksize = 0x10000 xts_key = pbkdf2.PBKDF2HMAC(hashes.SHA1(), 128 // 8 * 2, salt[0:40], 1000, default_backend()).derive(self._password) body = bytearray() for i in itertools.count(): offset_begin = i * header_blocksize offset_end = offset_begin + header_blocksize if offset_begin < len(plaintext_bytes): xts_cipher = Cipher(algorithms.AES(xts_key), modes.XTS(i.to_bytes(16, 'little')), default_backend()) xts_encryptor = xts_cipher.encryptor() block = plaintext_bytes[offset_begin:offset_end] if len(block) % 16 != 0: block += b'\x00' * (16 - len(block) % 16) body.extend( xts_encryptor.update(block) + xts_encryptor.finalize()) else: break xts_cipher = Cipher( algorithms.AES(xts_key), modes.XTS( b'\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00' ), default_backend()) xts_encryptor = xts_cipher.encryptor() header = xts_encryptor.update( header_tag + header_textlength.to_bytes(8, 'little') + header_blocksize.to_bytes(4, 'little')) + xts_encryptor.finalize() ciphertext_bytes = salt + header + body return base64.b64encode(ciphertext_bytes).decode('utf-8')
def encrypt_entry(self, data_arr, tweak_arr, encr_key): # Encrypt 32 bytes of data using AES-XTS encryption backend = default_backend() plain_text = data_arr.decode('hex') tweak = tweak_arr.decode('hex') cipher = Cipher(algorithms.AES(encr_key), modes.XTS(tweak), backend=backend) encryptor = cipher.encryptor() encrypted_data = encryptor.update(plain_text) return encrypted_data
def test_xts_vectors(self, vector, backend): key = binascii.unhexlify(vector["key"]) tweak = binascii.unhexlify(vector["i"]) pt = binascii.unhexlify(vector["pt"]) ct = binascii.unhexlify(vector["ct"]) cipher = base.Cipher(algorithms.AES(key), modes.XTS(tweak), backend) enc = cipher.encryptor() computed_ct = enc.update(pt) + enc.finalize() assert computed_ct == ct dec = cipher.decryptor() computed_pt = dec.update(ct) + dec.finalize() assert computed_pt == pt
def write(self, path, data, offset, fh, obfs=True): if obfs: obfs_path = self.path_obfuscate(path) else: obfs_path = path data_len = len(data) if path == self.proc_wl_path: wl_str = str(self.proc_wl) wl_str = wl_str[:offset] + data.decode() + wl_str[(offset + data_len):] wl_str = wl_str[wl_str.index("["):wl_str.index("]") + 1] self.proc_wl = ast.literal_eval(wl_str) return data_len if (obfs_path not in self.st_size_dict): self.st_size_dict[obfs_path] = 0 self.st_size_dict[obfs_path] = max(self.st_size_dict[obfs_path], offset + data_len) start_block = offset // self.block_size num_blocks = data_len // self.block_size if (data_len % self.block_size): num_blocks += 1 if num_blocks == 0: return 0 new_fh = os.open(obfs_path, os.O_RDONLY) rb_data = self.read_block(path, start_block, num_blocks, new_fh) os.close(new_fh) # new data if len(rb_data) == 0: rb_data = b"\0" * num_blocks * self.block_size start = offset % self.block_size rb_data = rb_data[:start] + data + rb_data[(start) + data_len:] # ct = rb_data # encryption here ct = b"" rb_data_len = len(rb_data) i = 0 while len(ct) < rb_data_len: pt = rb_data[:self.block_size] tweak1, tweak2 = start_block + i * self.block_size // 256, start_block + i * self.block_size % 256 # reusing keys with tweaks; consider modifying encryptor = Cipher(algorithms.AES(self.key), modes.XTS(struct.pack('>QQ', tweak1, tweak2)), backend=default_backend()).encryptor() ct += encryptor.update(pt) rb_data = rb_data[self.block_size:] i += 1 bytes_written = self.write_block(path, ct, start_block, fh) return data_len
def crack_keystore(keystore, dict): wordlist = open(dict, 'r') hash = get_hash_algorithm(keystore) count = 0 print("\n[*] Starting bruteforce...") for line in wordlist.readlines(): kdf1 = PBKDF2HMAC(algorithm=hash, length=keystore['Key_Length'], salt=keystore['Salt1_PBKDF2'], iterations=keystore['Iteration1_PBKDF2'], backend=backend) aes_key = kdf1.derive(line.rstrip().encode()) cipher = Cipher(algorithms.AES(aes_key), modes.XTS(tweak), backend=backend) decryptor = cipher.decryptor() aes_decrypt = decryptor.update(keystore['Enc_Password']) kdf2 = PBKDF2HMAC(algorithm=hash, length=keystore['KL2_PBKDF2'], salt=keystore['Salt2_PBKDF2'], iterations=keystore['Iteration2_PBKDF2'], backend=backend) final_hash = kdf2.derive(aes_decrypt) if random.randint(1, 20) == 12: print("\t%d password tested..." % count) count += 1 if binascii.hexlify(final_hash).decode() == binascii.hexlify( keystore['Final_Hash'].rstrip(b'\x00')).decode(): print("\n[*] Password Found = %s" % line.rstrip()) exit(0) print("\t[-] Password Not Found. You should try another dictionary.")
def mode_for_type(mode_type, iv_or_tweak=None): if mode_type == 'cbc': return modes.CBC(initialization_vector=iv_or_tweak) elif mode_type == 'xts': return modes.XTS(tweak=iv_or_tweak) elif mode_type == 'ecb': return modes.ECB() elif mode_type == 'ofb': return modes.OFB(initialization_vector=iv_or_tweak) elif mode_type == 'cfb': return modes.CFB(initialization_vector=iv_or_tweak) elif mode_type == 'cfb8': return modes.CFB8(initialization_vector=iv_or_tweak) elif mode_type == 'ctr': return modes.CTR(nonce=iv_or_tweak) elif mode_type == 'gcm': return modes.GCM(initialization_vector=iv_or_tweak) else: return None
def decrypt_data(data_input, decr_key, page_num, entry_no, entry_size): ''' Decrypt NVS data entry ''' page_max_size = 4096 first_entry_offset = 64 init_tweak_val = '0' tweak_len_needed = 32 # in hex tweak_tmp = '' data_input = binascii.hexlify(data_input) rel_addr = page_num * page_max_size + first_entry_offset # Set tweak value offset = entry_no * entry_size addr = hex(rel_addr + offset)[2:] addr_len = len(addr) if addr_len > 2: if not addr_len % 2: addr_tmp = addr else: addr_tmp = init_tweak_val + addr tweak_tmp = reverse_hexbytes(addr_tmp) tweak_val = tweak_tmp + (init_tweak_val * (tweak_len_needed - (len(tweak_tmp)))) else: tweak_val = addr + (init_tweak_val * (tweak_len_needed - len(addr))) if type(data_input) == bytes: data_input = data_input.decode() # Decrypt 32 bytes of data using AES-XTS decryption backend = default_backend() plain_text = codecs.decode(data_input, 'hex') tweak = codecs.decode(tweak_val, 'hex') cipher = Cipher(algorithms.AES(decr_key), modes.XTS(tweak), backend=backend) decryptor = cipher.decryptor() decrypted_data = decryptor.update(plain_text) return decrypted_data
def crack_keystore(keystore, wordlist): # Open wordlist file with open(wordlist, "r") as f: # Get hash and method from keystore hash_ = get_hash_algorithm(keystore) method = get_openssl_method(keystore) # Read each line of the file, it is the user password for user_password in f: user_password = user_password.strip() # First call to PBKDF2 EVP_password = hashlib.pbkdf2_hmac(hash_, user_password.encode(), keystore['pbkdf2_1_salt'], keystore['pbkdf2_1_iterations'], keystore['generic_key_length']) # Here, the password used for the second call to PBKDF2 is decrypted backend = default_backend() cipher = Cipher( algorithms.AES(EVP_password), modes.XTS( b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' ), backend=backend) decryptor = cipher.decryptor() decrypted_password = decryptor.update( keystore['pbkdf2_2_encrypted_password'] [0:keystore['evp_decrypt_input_length']]) + decryptor.finalize( ) # Final hash is computed final_hash = hashlib.pbkdf2_hmac(hash_, decrypted_password, keystore['pbkdf2_2_salt'], keystore['pbkdf2_2_iterations'], keystore['pbkdf2_2_key_length']) # If the computed hash is equal to the stored hash, then we have got the right user password if final_hash == keystore['final_hash']: return user_password return False
def xts_decrypt(iv, key, ciphertext): cipher = Cipher(algorithms.AES(key), modes.XTS(iv), backend=default_backend()) decryptor = cipher.decryptor() decodetext = decryptor.update(ciphertext) + decryptor.finalize() return decodetext
import binascii import os import pytest from cryptography.hazmat.backends.interfaces import CipherBackend from cryptography.hazmat.primitives.ciphers import algorithms, base, modes from .utils import _load_all_params, generate_encrypt_test from ...doubles import DummyMode from ...utils import load_nist_vectors @pytest.mark.supported( only_if=lambda backend: backend.cipher_supported( algorithms.AES(b"\x00" * 32), modes.XTS(b"\x00" * 16) ), skip_message="Does not support AES XTS", ) @pytest.mark.requires_backend_interface(interface=CipherBackend) class TestAESModeXTS(object): @pytest.mark.parametrize( "vector", # This list comprehension excludes any vector that does not have a # data unit length that is divisible by 8. The NIST vectors include # tests for implementations that support encryption of data that is # not divisible modulo 8, but OpenSSL is not such an implementation. [ x for x in _load_all_params( os.path.join("ciphers", "AES", "XTS", "tweak-128hexstr"),
def test_xts_tweak_not_bytes(self): with pytest.raises(TypeError): modes.XTS(32) # type: ignore[arg-type]
import binascii import os import pytest from cryptography.hazmat.backends.interfaces import CipherBackend from cryptography.hazmat.primitives.ciphers import algorithms, base, modes from .utils import _load_all_params, generate_aead_test, generate_encrypt_test from ...utils import load_nist_vectors @pytest.mark.supported( only_if=lambda backend: backend.cipher_supported( algorithms.AES(b"\x00" * 32), modes.XTS(b"\x00" * 16)), skip_message="Does not support AES XTS", ) @pytest.mark.requires_backend_interface(interface=CipherBackend) class TestAESModeXTS(object): @pytest.mark.parametrize( "vector", # This list comprehension excludes any vector that does not have a # data unit length that is divisible by 8. The NIST vectors include # tests for implementations that support encryption of data that is # not divisible modulo 8, but OpenSSL is not such an implementation. [ x for x in _load_all_params( os.path.join("ciphers", "AES", "XTS", "tweak-128hexstr"), ["XTSGenAES128.rsp", "XTSGenAES256.rsp"], load_nist_vectors) if int(x["dataunitlen"]) / 8.0 == int(x["dataunitlen"]) // 8
def _flash_encryption_operation_aes_xts(output_file, input_file, flash_address, keyfile, do_decrypt): """ Apply the AES-XTS algorithm with the hardware addressing scheme used by Espressif key = AES-XTS key (32 or 64 bytes) flash_address = address in flash to encrypt at. Must be multiple of 16 bytes. indata = Data to encrypt/decrypt. Must be multiple of 16 bytes. encrypt = True to Encrypt indata, False to decrypt indata. Returns a bitstring of the ciphertext or plaintext result. """ backend = default_backend() key = _load_hardware_key(keyfile) indata = input_file.read() if flash_address % 16 != 0: raise esptool.FatalError( "Starting flash address 0x%x must be a multiple of 16" % flash_address) if len(indata) % 16 != 0: raise esptool.FatalError( "Input data length (%d) must be a multiple of 16" % len(indata)) if len(indata) == 0: raise esptool.FatalError("Input data must be longer than 0") # left pad for a 1024-bit aligned address pad_left = flash_address % 0x80 indata = (b"\x00" * pad_left) + indata # right pad for full 1024-bit blocks pad_right = len(indata) % 0x80 if pad_right > 0: pad_right = 0x80 - pad_right indata = indata + (b"\x00" * pad_right) inblocks = _split_blocks(indata, 0x80) # split into 1024 bit blocks output = b"" for inblock in inblocks: # for each block tweak = struct.pack("<I", (flash_address & ~0x7F)) + (b"\x00" * 12) flash_address += 0x80 # for next block if len(tweak) != 16: raise esptool.FatalError( "Length of tweak must be 16, was {}".format(len(tweak))) cipher = Cipher(algorithms.AES(key), modes.XTS(tweak), backend=backend) encryptor = cipher.decryptor() if do_decrypt else cipher.encryptor() inblock = inblock[::-1] # reverse input outblock = encryptor.update(inblock) # standard algo output += outblock[::-1] # reverse output # undo any padding we applied to the input if pad_right != 0: output = output[:-pad_right] if pad_left != 0: output = output[pad_left:] # output length matches original input if len(output) != len(indata) - pad_left - pad_right: raise esptool.FatalError( "Length of input data ({}) should match the output data ({})". format(len(indata) - pad_left - pad_right, len(output))) output_file.write(output)
def test_xts_wrong_key_size(self, backend): with pytest.raises(ValueError): ciphers.Cipher(AES(b"0" * 16), modes.XTS(b"0" * 16), backend)
def test_xts_tweak_too_small(self): with pytest.raises(ValueError): modes.XTS(b"0")
def test_xts_tweak_not_bytes(self): with pytest.raises(TypeError): modes.XTS(32)
def aes_256_xts_encrypt_block(key, iv, blk): cipher = Cipher(algorithms.AES(key), modes.XTS(iv), backend=default_backend()) encryptor = cipher.encryptor() ct = encryptor.update(blk) + encryptor.finalize() return ct
def test_xts_no_duplicate_keys_encryption(self, backend): key = bytes(range(16)) * 2 tweak = b"\x00" * 16 cipher = base.Cipher(algorithms.AES(key), modes.XTS(tweak)) with pytest.raises(ValueError, match="duplicated keys"): cipher.encryptor()
def xts_encrypt(iv, key, plaintext): cipher = Cipher(algorithms.AES(key), modes.XTS(iv), backend=default_backend()) encryptor = cipher.encryptor() ciphertext = encryptor.update(plaintext) + encryptor.finalize() return ciphertext
_logfd.flush() else: def debugOutputToLog(*args): pass if not hasattr(fuse, '__version__'): raise RuntimeError( "your fuse-py doesn't know of fuse.__version__, probably it's too old." ) fuse.fuse_python_api = (0, 2) cipher = Cipher(algorithms.AES(AES_KEY), modes.XTS(XTS_TWEAK), backend=default_backend()) def filterLocals(fArgs): return dict(filter(lambda el: el[0] != 'self', fArgs.items())) class SB3Stat(Stat): def __init__(self): self.st_mode = 0 self.st_ino = 0 self.st_dev = 0 self.st_nlink = 0 self.st_uid = 0 self.st_gid = 0