Пример #1
0
def decrypt_stream(instream,
                   outstream,
                   password=None,
                   private_key=None,
                   public_key=None):

    session_key = None
    decryptor = None
    decrypt_stream.md5_digestor = None  # special kind of local variable...
    expected_md5_digest = None

    def outstream_writer_and_md5_digestor(decompressed_chunk):
        outstream.write(decompressed_chunk)
        if decrypt_stream.md5_digestor != None:
            decrypt_stream.md5_digestor.update(decompressed_chunk)

    # create session key and decryptor
    header = read_header(instream)
    if not header:
        LOGGER.info('failed to parse header; skipping file...')
        return
    # TODO: assert version and hash algo
    decrypt_stream.md5_digestor = hashlib.md5()
    if password != None:
        # password
        actual_password_hash = salted_hash_of(header['key1_hash'][:10],
                                              password)
        if header['key1_hash'] != actual_password_hash:
            LOGGER.warning('found key1_hash %s but expected %s',
                           actual_password_hash, header['key1_hash'])
        session_key = decrypted_with_password(
            base64.b64decode(header['enc_key1'].encode('ascii')), password,
            header['salt'].encode('ascii'))
        decryptor = decryptor_with_password(session_key)
    elif private_key != None and public_key != None:
        # RSA
        actual_public_key_hash = salted_hash_of(header['key2_hash'][:10],
                                                public_key)
        if header['key2_hash'] != actual_public_key_hash:
            LOGGER.warning('found key2_hash %s but expected %s',
                           actual_public_key_hash, header['key2_hash'])
        session_key = decrypted_with_private_key(
            base64.b64decode(header['enc_key2'].encode('ascii')), private_key)
        decryptor = decryptor_with_password(session_key)
    else:
        raise Exception("Key material is not found")
    actual_session_key_hash = salted_hash_of(header['session_key_hash'][:10],
                                             session_key)
    if header['session_key_hash'] != actual_session_key_hash:
        LOGGER.warning('found session_key_hash %s but expected %s',
                       actual_session_key_hash, header['session_key_hash'])

    # decrypt chunks
    data = b''
    with util.Lz4Decompressor(
            decompressed_chunk_handler=outstream_writer_and_md5_digestor
    ) as decompressor:
        for chunk in read_chunks(instream):
            if chunk['type'] == 'metadata':
                # decrypt file
                decrypted_data = decryptor_update(decryptor, data)
                decompressor.write(decrypted_data)
                #
                expected_md5_digest = chunk['file_md5']
                break
            elif chunk['type'] == 'data':
                data += chunk['data']
            else:
                raise Exception("Bad chunk found: " + str(chunk))
    # verify md5
    if decrypt_stream.md5_digestor != None and expected_md5_digest != None:
        actual_md5_digest = decrypt_stream.md5_digestor.hexdigest()
        if actual_md5_digest != expected_md5_digest:
            raise Exception('expected md5 digest %s but found %s',
                            expected_md5_digest, actual_md5_digest)
Пример #2
0
def lz4_uncompress(compressed_data):
        result = io.BytesIO()
        with util.Lz4Decompressor(decompressed_chunk_handler=result.write) as decompressor:
                decompressor.write(compressed_data)
        return result.getvalue()
Пример #3
0
def decrypt_stream(instream, outstream, password=None, private_key=None):

        session_key = None
        decryptor = None
        decrypt_stream.md5_digestor = None # special kind of local variable...
        expected_md5_digest = None
        enc_key1_bytes = None
        enc_key2_bytes = None
        salt = b''
        session_key_hash = None

        def outstream_writer_and_md5_digestor(decompressed_chunk):
                outstream.write(decompressed_chunk)
                if decrypt_stream.md5_digestor != None:
                        decrypt_stream.md5_digestor.update(decompressed_chunk)

        with util.Lz4Decompressor(decompressed_chunk_handler=outstream_writer_and_md5_digestor) as decompressor:
                for (key,value) in decode_csenc_stream(instream):
                        for case in switch(key):
                                if case('digest'):
                                        if value != 'md5':
                                                LOGGER.warning('found unexpected digest "%s": cannot verify checksum', value)
                                        decrypt_stream.md5_digestor = hashlib.md5()
                                        break
                                if case('enc_key1'):
                                        enc_key1_bytes = base64.b64decode(value.encode('ascii'))
                                        break
                                if case('enc_key2'):
                                        enc_key2_bytes = base64.b64decode(value.encode('ascii'))
                                        break
                                if case('key1_hash'):
                                        if password != None:
                                                actual_password_hash = salted_hash_of(value[:10], password)
                                                if value != actual_password_hash:
                                                        LOGGER.warning('found key1_hash %s but expected %s', actual_password_hash, value)
                                        break
                                if case('key2_hash'):
                                        # TODO: verify some public/private key pair hash here
                                        break
                                if case('salt'):
                                        salt = value.encode('ascii')
                                        assert isinstance(salt, bytes)
                                        break
                                if case('session_key_hash'):
                                        session_key_hash = value
                                        break
                                if case('version'):
                                        version = value
                                        expected_version_numbers = [OrderedDict([('major',1),('minor',0)]), OrderedDict([('major',3),('minor',0)])]
                                        if version not in expected_version_numbers:
                                                raise Exception('found version number ' + str(value) + \
                                                        ' instead of one of the expected ' + str(expected_version_numbers))
                                        if (version['major'] > 1) != (salt != b''):
                                                version_string = '%d.%d' % (version['major'], version['minor'])
                                                LOGGER.warning('salt is expected in version 3+ (version: %s, salt present: %s)', version_string, (salt != b''))
                                        break
                                if case(None):
                                        if decryptor == None:
                                                if password != None and enc_key1_bytes != None:
                                                        session_key = decrypted_with_password(enc_key1_bytes, password, salt)
                                                elif private_key != None and enc_key2_bytes != None:
                                                        session_key = decrypted_with_private_key(enc_key2_bytes, private_key)
                                                if session_key == None:
                                                        raise Exception('not enough information to decrypt data: need either password and enc_key1 or private key and enc_key2')
                                                if session_key_hash == None:
                                                        LOGGER.warning('did not find session_key_hash to verify the session key')
                                                else:
                                                        actual_session_key_hash = salted_hash_of(session_key_hash[:10], session_key)
                                                        if session_key_hash != actual_session_key_hash:
                                                                LOGGER.warning('found session_key_hash %s but expected %s', actual_session_key_hash, session_key_hash)
                                                decryptor = decryptor_with_password(binascii.unhexlify(session_key) if salt else session_key, salt=b'')
                                        decrypted_chunk = decryptor_update(decryptor, value)
                                        decompressor.write(decrypted_chunk)
                                        break
                                if case('file_md5'):
                                        expected_md5_digest = value
                                        break

        if decrypt_stream.md5_digestor != None and expected_md5_digest != None:
                actual_md5_digest = decrypt_stream.md5_digestor.hexdigest()
                if actual_md5_digest != expected_md5_digest:
                        raise Exception('expected md5 digest %s but found %s', expected_md5_digest, actual_md5_digest)
Пример #4
0
def decrypt_stream(instream, outstream, password=None, private_key=None):

        session_key = None
        decryptor = None
        decrypt_stream.md5_digestor = None # special kind of local variable...
        expected_md5_digest = None

        def outstream_writer_and_md5_digestor(decompressed_chunk):
                outstream.write(decompressed_chunk)
                if decrypt_stream.md5_digestor != None:
                        decrypt_stream.md5_digestor.update(decompressed_chunk)

        with util.Lz4Decompressor(decompressed_chunk_handler=outstream_writer_and_md5_digestor) as decompressor:
                for (key,value) in decode_csenc_stream(instream):
                        for case in switch(key):
                                if case('digest'):
                                        if value != 'md5':
                                                LOGGER.warning('found unexpected digest "%s": cannot verify checksum', value)
                                        decrypt_stream.md5_digestor = hashlib.md5()
                                        break
                                if case('enc_key1'):
                                        if password != None:
                                                session_key = decrypted_with_password(base64.b64decode(value.encode('ascii')), password)
                                                decryptor = decryptor_with_password(session_key)
                                        break
                                if case('enc_key2'):
                                        if private_key != None:
                                                session_key = decrypted_with_private_key(base64.b64decode(value.encode('ascii')), private_key)
                                                decryptor = decryptor_with_password(session_key)
                                        break
                                if case('key1_hash'):
                                        if password != None:
                                                actual_password_hash = salted_hash_of(value[:10], password)
                                                if value != actual_password_hash:
                                                        LOGGER.warning('found key1_hash %s but expected %s', actual_password_hash, value)
                                        break
                                if case('key2_hash'):
                                        # TODO: verify some public/private key pair hash here
                                        break
                                if case('session_key_hash'):
                                        if session_key != None:
                                                actual_session_key_hash = salted_hash_of(value[:10], session_key)
                                                if value != actual_session_key_hash:
                                                        LOGGER.warning('found session_key_hash %s but expected %s', actual_session_key_hash, value)
                                        break
                                if case('version'):
                                        expected_version_number = OrderedDict([('major',1),('minor',0)])
                                        if value != expected_version_number:
                                                raise Exception('found version number ' + str(value) + \
                                                        ' instead of expected ' + str(expected_version_number))
                                        break
                                if case(None):
                                        if decryptor == None:
                                                raise Exception('not enough information to decrypt data')
                                        decrypted_chunk = decryptor_update(decryptor, value)
                                        decompressor.write(decrypted_chunk)
                                        break
                                if case('file_md5'):
                                        expected_md5_digest = value
                                        break

        if decrypt_stream.md5_digestor != None and expected_md5_digest != None:
                actual_md5_digest = decrypt_stream.md5_digestor.hexdigest()
                if actual_md5_digest != expected_md5_digest:
                        raise Exception('expected md5 digest %s but found %s', expected_md5_digest, actual_md5_digest)