Skip to content

Commit

Permalink
Comment on more code.
Browse files Browse the repository at this point in the history
  • Loading branch information
phyber committed Nov 4, 2017
1 parent 7a57fb2 commit 28aa546
Showing 1 changed file with 28 additions and 8 deletions.
36 changes: 28 additions & 8 deletions tibudecrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@


def pkcs5_unpad(chunk):
"""Return data after PKCS5 unpadding
"""
Return data after PKCS5 unpadding
With python3 bytes are already treated as arrays of ints so
we don't have to convert them with ord.
"""
Expand All @@ -104,14 +105,14 @@ def pkcs5_unpad(chunk):
else:
padding_length = chunk[-1]

# cite https://stackoverflow.com/a/20457519
# Cite https://stackoverflow.com/a/20457519
if padding_length < 1 or padding_length > Crypto.Cipher.AES.block_size:
raise ValueError("bad decrypt pad (%d)" % padding_length)

# all the pad-bytes must be the same
expected_bytes = chr(padding_length).encode('ascii') * padding_length
if chunk[-padding_length:] != expected_bytes:
# this is similar to the bad decrypt:evp_enc.c from openssl program
# This is similar to the bad decrypt:evp_enc.c from openssl program
raise ValueError("bad decrypt")

return chunk[:-padding_length]
Expand Down Expand Up @@ -165,11 +166,16 @@ def check_password(self, password):
Performs HMAC password verification and hashes the password
for use when decrypting the private key and session key.
"""
# Get the sha1 HMAC of the password.
mac = hmac.new(
self.pass_hmac_key,
password,
hashlib.sha1)

# Verify that the mac that we get matches what we expect.
if mac.digest() == self.pass_hmac_result:
# Get the sha1 hash of the password and pad out to 32 chars with
# 0x00.
sha1 = hashlib.sha1()
sha1.update(password)
self.hashed_pass = sha1.digest().ljust(
Expand All @@ -185,16 +191,17 @@ def read_file(self):
"""
try:
with open(self.filename, 'rb') as in_file:
in_file.readline() # skip the header
in_file.readline() # Header, can be ignored.
pass_hmac_key = in_file.readline()
pass_hmac_result = in_file.readline()
in_file.readline() # dummy public key
in_file.readline() # Dummy public key, can be ignored.
enc_privkey_spec = in_file.readline()
enc_sesskey_spec = in_file.readline()

self.encrypted_data_start_byte_offset = in_file.tell()
in_file.close()

# All of the above are base64 encoded, decode them.
self.pass_hmac_key = base64.b64decode(pass_hmac_key)
self.pass_hmac_result = base64.b64decode(pass_hmac_result)
self.enc_privkey_spec = base64.b64decode(enc_privkey_spec)
Expand All @@ -203,20 +210,29 @@ def read_file(self):
raise

def setup_crypto(self):
"""
Decrypts the various keys and gets us to the stage where we can decrypt
the data.
"""
# Get a cipher for decrypting the private key with the user's password.
cipher = Crypto.Cipher.AES.new(
self.hashed_pass,
mode=Crypto.Cipher.AES.MODE_CBC,
IV=TIBU_IV)

# Decrypt the private key.
dec_privkey_spec = pkcs5_unpad(cipher.decrypt(self.enc_privkey_spec))

rsa_privkey = Crypto.PublicKey.RSA.importKey(
dec_privkey_spec)
# Import the private key
rsa_privkey = Crypto.PublicKey.RSA.importKey(dec_privkey_spec)

# Use the private key to get a cipher for decrypting the session key.
cipher = Crypto.Cipher.PKCS1_v1_5.new(rsa_privkey)
dec_sesskey = cipher.decrypt(
self.enc_sesskey_spec,
None)

# Finally, use the session key to get a cipher for decrypting the data.
self.cipher = Crypto.Cipher.AES.new(
dec_sesskey,
mode=Crypto.Cipher.AES.MODE_CBC,
Expand All @@ -239,8 +255,10 @@ def main(args):

try:
password = args.get('<password>')

if password is None:
password = getpass.getpass()

encrypted_file.check_password(password.encode('utf-8'))
except PasswordMismatchError as exc:
return "Error: {e}".format(e=exc)
Expand All @@ -255,7 +273,9 @@ def main(args):
in_file.seek(encrypted_file.encrypted_data_start_byte_offset, 0)

while not finished:
chunk, next_chunk = next_chunk, encrypted_file.cipher.decrypt(in_file.read(1024 * Crypto.Cipher.AES.block_size))
# Read and decrypt a chunk of encrypted data.
enc_data = in_file.read(1024 * Crypto.Cipher.AES.block_size)
chunk, next_chunk = next_chunk, encrypted_file.cipher.decrypt(enc_data)

# On the first iteration, we won't have a chunk. Skip it.
if chunk is None:
Expand Down

0 comments on commit 28aa546

Please sign in to comment.