]> xmof Git - DeDRM.git/commitdiff
Remove AlfCrypto libraries and perform everything in Python
authorNoDRM <no_drm123@protonmail.com>
Sat, 6 Aug 2022 18:13:19 +0000 (20:13 +0200)
committerNoDRM <no_drm123@protonmail.com>
Sat, 6 Aug 2022 18:13:19 +0000 (20:13 +0200)
The old AlfCrypto DLL, SO and DYLIB files are ancient,
I don't have the systems to recompile them all, they
cause issues on ARM Macs, and I doubt with all the Python
improvements over the last years that they have a significant
performance advantage. And even if that's the case, nobody is
importing hundreds of DRM books at the same time so it shouldn't
hurt if some decryptions might take a bit longer.

DeDRM_plugin/__init__.py
DeDRM_plugin/alfcrypto.dll [deleted file]
DeDRM_plugin/alfcrypto.py
DeDRM_plugin/alfcrypto64.dll [deleted file]
DeDRM_plugin/alfcrypto_src.zip [deleted file]
DeDRM_plugin/libalfcrypto.dylib [deleted file]
DeDRM_plugin/libalfcrypto32.so [deleted file]
DeDRM_plugin/libalfcrypto64.so [deleted file]
DeDRM_plugin/mobidedrm.py
DeDRM_plugin/subasyncio.py [deleted file]

index eaf505a24f10b770cecd1e2def2a633ea9b8b547..913d05e3f3b3098bf0e6f85258282c7659f4effd 100644 (file)
@@ -187,15 +187,12 @@ class DeDRM(FileTypePlugin):
                 os.mkdir(self.alfdir)
             # only continue if we've never run this version of the plugin before
             self.verdir = os.path.join(self.maindir,PLUGIN_VERSION)
-            if not os.path.exists(self.verdir):
-                if iswindows:
-                    names = ["alfcrypto.dll","alfcrypto64.dll"]
-                elif isosx:
-                    names = ["libalfcrypto.dylib"]
-                else:
-                    names = ["libalfcrypto32.so","libalfcrypto64.so","kindlekey.py","adobekey.py","subasyncio.py"]
+            if not os.path.exists(self.verdir) and not iswindows and not isosx:
+
+                names = ["kindlekey.py","adobekey.py","ignoblekeyNookStudy.py"]
+
                 lib_dict = self.load_resources(names)
-                print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION))
+                print("{0} v{1}: Copying needed Python scripts from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION))
 
                 for entry, data in lib_dict.items():
                     file_path = os.path.join(self.alfdir, entry)
diff --git a/DeDRM_plugin/alfcrypto.dll b/DeDRM_plugin/alfcrypto.dll
deleted file mode 100644 (file)
index 26d740d..0000000
Binary files a/DeDRM_plugin/alfcrypto.dll and /dev/null differ
index 5a31d095c3536319f387a778c854d815a6e6da67..ecb79166a6c52aa42a886c6b44c05de62da9e900 100644 (file)
 # pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm>
 # pbkdf2.py This code may be freely used and modified for any purpose.
 
-import sys, os
 import hmac
 from struct import pack
 import hashlib
-
-# interface to needed routines libalfcrypto
-def _load_libalfcrypto():
-    import ctypes
-    from ctypes import CDLL, byref, POINTER, c_void_p, c_char_p, c_int, c_long, \
-        Structure, c_ulong, create_string_buffer, addressof, string_at, cast, sizeof
-
-    pointer_size = ctypes.sizeof(ctypes.c_voidp)
-    name_of_lib = None
-    if sys.platform.startswith('darwin'):
-        name_of_lib = 'libalfcrypto.dylib'
-    elif sys.platform.startswith('win'):
-        if pointer_size == 4:
-            name_of_lib = 'alfcrypto.dll'
-        else:
-            name_of_lib = 'alfcrypto64.dll'
-    else:
-        if pointer_size == 4:
-            name_of_lib = 'libalfcrypto32.so'
-        else:
-            name_of_lib = 'libalfcrypto64.so'
-
-    # hard code to local location for libalfcrypto
-    libalfcrypto = os.path.join(sys.path[0],name_of_lib)
-    if not os.path.isfile(libalfcrypto):
-        libalfcrypto = os.path.join(sys.path[0], 'lib', name_of_lib)
-    if not os.path.isfile(libalfcrypto):
-        libalfcrypto = os.path.join('.',name_of_lib)
-    if not os.path.isfile(libalfcrypto):
-        raise Exception('libalfcrypto not found at %s' % libalfcrypto)
-
-    libalfcrypto = CDLL(libalfcrypto)
-
-    c_char_pp = POINTER(c_char_p)
-    c_int_p = POINTER(c_int)
-
-
-    def F(restype, name, argtypes):
-        func = getattr(libalfcrypto, name)
-        func.restype = restype
-        func.argtypes = argtypes
-        return func
-
-    # aes cbc decryption
-    #
-    # struct aes_key_st {
-    # unsigned long rd_key[4 *(AES_MAXNR + 1)];
-    # int rounds;
-    # };
-    #
-    # typedef struct aes_key_st AES_KEY;
-    #
-    # int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
-    #
-    #
-    # void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
-    # const unsigned long length, const AES_KEY *key,
-    # unsigned char *ivec, const int enc);
-
-    AES_MAXNR = 14
-
-    class AES_KEY(Structure):
-        _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))), ('rounds', c_int)]
-
-    AES_KEY_p = POINTER(AES_KEY)
-    AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p, c_int])
-    AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p])
-
-
-
-    # Pukall 1 Cipher
-    # unsigned char *PC1(const unsigned char *key, unsigned int klen, const unsigned char *src,
-    #                unsigned char *dest, unsigned int len, int decryption);
-
-    PC1 = F(c_char_p, 'PC1', [c_char_p, c_ulong, c_char_p, c_char_p, c_ulong, c_ulong])
-
-    # Topaz Encryption
-    # typedef struct _TpzCtx {
-    #    unsigned int v[2];
-    # } TpzCtx;
-    #
-    # void topazCryptoInit(TpzCtx *ctx, const unsigned char *key, int klen);
-    # void topazCryptoDecrypt(const TpzCtx *ctx, const unsigned char *in, unsigned char *out, int len);
-
-    class TPZ_CTX(Structure):
-        _fields_ = [('v', c_long * 2)]
-
-    TPZ_CTX_p = POINTER(TPZ_CTX)
-    topazCryptoInit = F(None, 'topazCryptoInit', [TPZ_CTX_p, c_char_p, c_ulong])
-    topazCryptoDecrypt = F(None, 'topazCryptoDecrypt', [TPZ_CTX_p, c_char_p, c_char_p, c_ulong])
-
-
-    class AES_CBC(object):
-        def __init__(self):
-            self._blocksize = 0
-            self._keyctx = None
-            self._iv = 0
-
-        def set_decrypt_key(self, userkey, iv):
-            self._blocksize = len(userkey)
-            if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
-                raise Exception('AES CBC improper key used')
-                return
-            keyctx = self._keyctx = AES_KEY()
-            self._iv = iv
-            rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx)
-            if rv < 0:
-                raise Exception('Failed to initialize AES CBC key')
-
-        def decrypt(self, data):
-            out = create_string_buffer(len(data))
-            mutable_iv = create_string_buffer(self._iv, len(self._iv))
-            rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, mutable_iv, 0)
-            if rv == 0:
-                raise Exception('AES CBC decryption failed')
-            return out.raw
-
-    class Pukall_Cipher(object):
-        def __init__(self):
-            self.key = None
-
-        def PC1(self, key, src, decryption=True):
-            self.key = key
-            out = create_string_buffer(len(src))
-            de = 0
+import aescbc
+
+class Pukall_Cipher(object):
+    def __init__(self):
+        self.key = None
+
+    def PC1(self, key, src, decryption=True):
+        sum1 = 0;
+        sum2 = 0;
+        keyXorVal = 0;
+        if len(key)!=16:
+            raise Exception("PC1: Bad key length")
+        wkey = []
+        for i in range(8):
+            wkey.append(key[i*2]<<8 | key[i*2+1])
+        dst = bytearray(len(src))
+        for i in range(len(src)):
+            temp1 = 0;
+            byteXorVal = 0;
+            for j in range(8):
+                temp1 ^= wkey[j]
+                sum2  = (sum2+j)*20021 + sum1
+                sum1  = (temp1*346)&0xFFFF
+                sum2  = (sum2+sum1)&0xFFFF
+                temp1 = (temp1*20021+1)&0xFFFF
+                byteXorVal ^= temp1 ^ sum2
+            curByte = src[i]
+            if not decryption:
+                keyXorVal = curByte * 257;
+            curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
             if decryption:
-                de = 1
-            rv = PC1(key, len(key), src, out, len(src), de)
-            return out.raw
-
-    class Topaz_Cipher(object):
-        def __init__(self):
-            self._ctx = None
-
-        def ctx_init(self, key):
-            tpz_ctx = self._ctx = TPZ_CTX()
-            topazCryptoInit(tpz_ctx, key, len(key))
-            return tpz_ctx
-
-        def decrypt(self, data,  ctx=None):
-            if ctx == None:
-                ctx = self._ctx
-            out = create_string_buffer(len(data))
-            topazCryptoDecrypt(ctx, data, out, len(data))
-            return out.raw
-
-    print("Using Library AlfCrypto DLL/DYLIB/SO")
-    return (AES_CBC, Pukall_Cipher, Topaz_Cipher)
-
-
-def _load_python_alfcrypto():
-
-    import aescbc
-
-    class Pukall_Cipher(object):
-        def __init__(self):
-            self.key = None
-
-        def PC1(self, key, src, decryption=True):
-            sum1 = 0;
-            sum2 = 0;
-            keyXorVal = 0;
-            if len(key)!=16:
-                raise Exception('Pukall_Cipher: Bad key length.')
-            wkey = []
-            for i in range(8):
-                wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
-            dst = ""
-            for i in range(len(src)):
-                temp1 = 0;
-                byteXorVal = 0;
-                for j in range(8):
-                    temp1 ^= wkey[j]
-                    sum2  = (sum2+j)*20021 + sum1
-                    sum1  = (temp1*346)&0xFFFF
-                    sum2  = (sum2+sum1)&0xFFFF
-                    temp1 = (temp1*20021+1)&0xFFFF
-                    byteXorVal ^= temp1 ^ sum2
-                curByte = ord(src[i])
-                if not decryption:
-                    keyXorVal = curByte * 257;
-                curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
-                if decryption:
-                    keyXorVal = curByte * 257;
-                for j in range(8):
-                    wkey[j] ^= keyXorVal;
-                dst+=chr(curByte)
-            return dst
-
-    class Topaz_Cipher(object):
-        def __init__(self):
-            self._ctx = None
-
-        def ctx_init(self, key):
-            ctx1 = 0x0CAFFE19E
-            if isinstance(key, str):
-                key = key.encode('latin-1')
-            for keyByte in key:
-                ctx2 = ctx1
-                ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF )
-            self._ctx = [ctx1, ctx2]
-            return [ctx1,ctx2]
-
-        def decrypt(self, data,  ctx=None):
-            if ctx == None:
-                ctx = self._ctx
-            ctx1 = ctx[0]
-            ctx2 = ctx[1]
-            plainText = ""
-            if isinstance(data, str):
-                data = data.encode('latin-1')
-            for dataByte in data:
-                m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF
-                ctx2 = ctx1
-                ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF)
-                plainText += chr(m)
-            return plainText
-
-    class AES_CBC(object):
-        def __init__(self):
-            self._key = None
-            self._iv = None
-            self.aes = None
-
-        def set_decrypt_key(self, userkey, iv):
-            self._key = userkey
-            self._iv = iv
-            self.aes = aescbc.AES_CBC(userkey, aescbc.noPadding(), len(userkey))
-
-        def decrypt(self, data):
-            iv = self._iv
-            cleartext = self.aes.decrypt(iv + data)
-            return cleartext
-
-    print("Using Library AlfCrypto Python")
-    return (AES_CBC, Pukall_Cipher, Topaz_Cipher)
-
-
-def _load_crypto():
-    AES_CBC = Pukall_Cipher = Topaz_Cipher = None
-    cryptolist = (_load_libalfcrypto, _load_python_alfcrypto)
-    for loader in cryptolist:
-        try:
-            AES_CBC, Pukall_Cipher, Topaz_Cipher = loader()
-            break
-        except (ImportError, Exception):
-            pass
-    return AES_CBC, Pukall_Cipher, Topaz_Cipher
-
-AES_CBC, Pukall_Cipher, Topaz_Cipher = _load_crypto()
+                keyXorVal = curByte * 257;
+            for j in range(8):
+                wkey[j] ^= keyXorVal;
+            dst[i] = curByte
+        return bytes(dst)
+
+class Topaz_Cipher(object):
+    def __init__(self):
+        self._ctx = None
+
+    def ctx_init(self, key):
+        ctx1 = 0x0CAFFE19E
+        if isinstance(key, str):
+            key = key.encode('latin-1')
+        for keyByte in key:
+            ctx2 = ctx1
+            ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF )
+        self._ctx = [ctx1, ctx2]
+        return [ctx1,ctx2]
+
+    def decrypt(self, data,  ctx=None):
+        if ctx == None:
+            ctx = self._ctx
+        ctx1 = ctx[0]
+        ctx2 = ctx[1]
+        plainText = ""
+        if isinstance(data, str):
+            data = data.encode('latin-1')
+        for dataByte in data:
+            m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF
+            ctx2 = ctx1
+            ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF)
+            plainText += chr(m)
+        return plainText
+
+class AES_CBC(object):
+    def __init__(self):
+        self._key = None
+        self._iv = None
+        self.aes = None
+
+    def set_decrypt_key(self, userkey, iv):
+        self._key = userkey
+        self._iv = iv
+        self.aes = aescbc.AES_CBC(userkey, aescbc.noPadding(), len(userkey))
+
+    def decrypt(self, data):
+        iv = self._iv
+        cleartext = self.aes.decrypt(iv + data)
+        return cleartext
 
 
 class KeyIVGen(object):
diff --git a/DeDRM_plugin/alfcrypto64.dll b/DeDRM_plugin/alfcrypto64.dll
deleted file mode 100644 (file)
index 7bef68e..0000000
Binary files a/DeDRM_plugin/alfcrypto64.dll and /dev/null differ
diff --git a/DeDRM_plugin/alfcrypto_src.zip b/DeDRM_plugin/alfcrypto_src.zip
deleted file mode 100644 (file)
index 269810c..0000000
Binary files a/DeDRM_plugin/alfcrypto_src.zip and /dev/null differ
diff --git a/DeDRM_plugin/libalfcrypto.dylib b/DeDRM_plugin/libalfcrypto.dylib
deleted file mode 100644 (file)
index 01c348c..0000000
Binary files a/DeDRM_plugin/libalfcrypto.dylib and /dev/null differ
diff --git a/DeDRM_plugin/libalfcrypto32.so b/DeDRM_plugin/libalfcrypto32.so
deleted file mode 100644 (file)
index 9a5a442..0000000
Binary files a/DeDRM_plugin/libalfcrypto32.so and /dev/null differ
diff --git a/DeDRM_plugin/libalfcrypto64.so b/DeDRM_plugin/libalfcrypto64.so
deleted file mode 100644 (file)
index a08ac28..0000000
Binary files a/DeDRM_plugin/libalfcrypto64.so and /dev/null differ
index fe761c4ae7de1113287fd1a50a4bbf6821c14b71..22cdb35179ecd448c42cda6e9fd1f120084c5931 100755 (executable)
@@ -80,10 +80,7 @@ import sys
 import os
 import struct
 import binascii
-try:
-    from alfcrypto import Pukall_Cipher
-except:
-    print("AlfCrypto not found. Using python PC1 implementation.")
+from alfcrypto import Pukall_Cipher
 
 from utilities import SafeUnbuffered
 
@@ -140,41 +137,8 @@ def PC1(key, src, decryption=True):
     # if we can get it from alfcrypto, use that
     try:
         return Pukall_Cipher().PC1(key,src,decryption)
-    except NameError:
-        pass
-    except TypeError:
-        pass
-
-    # use slow python version, since Pukall_Cipher didn't load
-    sum1 = 0;
-    sum2 = 0;
-    keyXorVal = 0;
-    if len(key)!=16:
-         DrmException ("PC1: Bad key length")
-    wkey = []
-    for i in range(8):
-        wkey.append(key[i*2]<<8 | key[i*2+1])
-    dst = bytearray(len(src))
-    for i in range(len(src)):
-        temp1 = 0;
-        byteXorVal = 0;
-        for j in range(8):
-            temp1 ^= wkey[j]
-            sum2  = (sum2+j)*20021 + sum1
-            sum1  = (temp1*346)&0xFFFF
-            sum2  = (sum2+sum1)&0xFFFF
-            temp1 = (temp1*20021+1)&0xFFFF
-            byteXorVal ^= temp1 ^ sum2
-        curByte = src[i]
-        if not decryption:
-            keyXorVal = curByte * 257;
-        curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
-        if decryption:
-            keyXorVal = curByte * 257;
-        for j in range(8):
-            wkey[j] ^= keyXorVal;
-        dst[i] = curByte
-    return bytes(dst)
+    except: 
+        raise
 
 # accepts unicode returns unicode
 def checksumPid(s):
@@ -232,12 +196,7 @@ class MobiBook:
         pass
 
     def __init__(self, infile):
-        print("MobiDeDrm v{0:s}.\nCopyright © 2008-2020 The Dark Reverser, Apprentice Harper et al.".format(__version__))
-
-        try:
-            from alfcrypto import Pukall_Cipher
-        except:
-            print("AlfCrypto not found. Using python PC1 implementation.")
+        print("MobiDeDrm v{0:s}.\nCopyright © 2008-2022 The Dark Reverser, Apprentice Harper et al.".format(__version__))
 
         # initial sanity check on file
         self.data_file = open(infile, 'rb').read()
diff --git a/DeDRM_plugin/subasyncio.py b/DeDRM_plugin/subasyncio.py
deleted file mode 100644 (file)
index de084d3..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-#!/usr/bin/env python
-# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-
-import os, sys
-import signal
-import threading
-import subprocess
-from subprocess import Popen, PIPE, STDOUT
-
-# **heavily** chopped up and modfied version of asyncproc.py
-# to make it actually work on Windows as well as Mac/Linux
-# For the original see:
-# "http://www.lysator.liu.se/~bellman/download/"
-# author is  "Thomas Bellman <bellman@lysator.liu.se>"
-# available under GPL version 3 or Later
-
-# create an asynchronous subprocess whose output can be collected in
-# a non-blocking manner
-
-# What a mess!  Have to use threads just to get non-blocking io
-# in a cross-platform manner
-
-# luckily all thread use is hidden within this class
-
-class Process(object):
-    def __init__(self, *params, **kwparams):
-        if len(params) <= 3:
-            kwparams.setdefault('stdin', subprocess.PIPE)
-        if len(params) <= 4:
-            kwparams.setdefault('stdout', subprocess.PIPE)
-        if len(params) <= 5:
-            kwparams.setdefault('stderr', subprocess.PIPE)
-        self.__pending_input = []
-        self.__collected_outdata = []
-        self.__collected_errdata = []
-        self.__exitstatus = None
-        self.__lock = threading.Lock()
-        self.__inputsem = threading.Semaphore(0)
-        self.__quit = False
-
-        self.__process = subprocess.Popen(*params, **kwparams)
-
-        if self.__process.stdin:
-            self.__stdin_thread = threading.Thread(
-                name="stdin-thread",
-                target=self.__feeder, args=(self.__pending_input,
-                                            self.__process.stdin))
-            self.__stdin_thread.setDaemon(True)
-            self.__stdin_thread.start()
-
-        if self.__process.stdout:
-            self.__stdout_thread = threading.Thread(
-                name="stdout-thread",
-                target=self.__reader, args=(self.__collected_outdata,
-                                            self.__process.stdout))
-            self.__stdout_thread.setDaemon(True)
-            self.__stdout_thread.start()
-
-        if self.__process.stderr:
-            self.__stderr_thread = threading.Thread(
-                name="stderr-thread",
-                target=self.__reader, args=(self.__collected_errdata,
-                                            self.__process.stderr))
-            self.__stderr_thread.setDaemon(True)
-            self.__stderr_thread.start()
-
-    def pid(self):
-        return self.__process.pid
-
-    def kill(self, signal):
-        self.__process.send_signal(signal)
-
-    # check on subprocess (pass in 'nowait') to act like poll
-    def wait(self, flag):
-        if flag.lower() == 'nowait':
-            rc = self.__process.poll()
-        else:
-            rc = self.__process.wait()
-        if rc != None:
-            if self.__process.stdin:
-                self.closeinput()
-            if self.__process.stdout:
-                self.__stdout_thread.join()
-            if self.__process.stderr:
-                self.__stderr_thread.join()
-        return self.__process.returncode
-
-    def terminate(self):
-        if self.__process.stdin:
-            self.closeinput()
-        self.__process.terminate()
-
-    # thread gets data from subprocess stdout
-    def __reader(self, collector, source):
-        while True:
-            data = os.read(source.fileno(), 65536)
-            self.__lock.acquire()
-            collector.append(data)
-            self.__lock.release()
-            if data == "":
-                source.close()
-                break
-        return
-
-    # thread feeds data to subprocess stdin
-    def __feeder(self, pending, drain):
-        while True:
-            self.__inputsem.acquire()
-            self.__lock.acquire()
-            if not pending  and self.__quit:
-                drain.close()
-                self.__lock.release()
-                break
-            data = pending.pop(0)
-            self.__lock.release()
-            drain.write(data)
-
-    # non-blocking read of data from subprocess stdout
-    def read(self):
-        self.__lock.acquire()
-        outdata = "".join(self.__collected_outdata)
-        del self.__collected_outdata[:]
-        self.__lock.release()
-        return outdata
-
-    # non-blocking read of data from subprocess stderr
-    def readerr(self):
-        self.__lock.acquire()
-        errdata = "".join(self.__collected_errdata)
-        del self.__collected_errdata[:]
-        self.__lock.release()
-        return errdata
-
-    # non-blocking write to stdin of subprocess
-    def write(self, data):
-        if self.__process.stdin is None:
-            raise ValueError("Writing to process with stdin not a pipe")
-        self.__lock.acquire()
-        self.__pending_input.append(data)
-        self.__inputsem.release()
-        self.__lock.release()
-
-    # close stdinput of subprocess
-    def closeinput(self):
-        self.__lock.acquire()
-        self.__quit = True
-        self.__inputsem.release()
-        self.__lock.release()