KoboDrmRemover.py 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738
  1. from Crypto.Cipher import AES
  2. from Crypto.Util import Padding
  3. from typing import Dict
  4. import base64
  5. import binascii
  6. import hashlib
  7. import zipfile
  8. # Based on obok.py by Physisticated.
  9. class KoboDrmRemover:
  10. def __init__( self, deviceId: str, userId: str ):
  11. self.DeviceIdUserIdKey = KoboDrmRemover.__MakeDeviceIdUserIdKey( deviceId, userId )
  12. @staticmethod
  13. def __MakeDeviceIdUserIdKey( deviceId: str, userId: str ) -> bytes:
  14. deviceIdUserId = ( deviceId + userId ).encode()
  15. key = hashlib.sha256( deviceIdUserId ).hexdigest()
  16. return binascii.a2b_hex( key[ 32: ] )
  17. def __DecryptContents( self, contents: bytes, contentKeyBase64: str ) -> bytes:
  18. contentKey = base64.b64decode( contentKeyBase64 )
  19. keyAes = AES.new( self.DeviceIdUserIdKey, AES.MODE_ECB )
  20. decryptedContentKey = keyAes.decrypt( contentKey )
  21. contentAes = AES.new( decryptedContentKey, AES.MODE_ECB )
  22. decryptedContents = contentAes.decrypt( contents )
  23. return Padding.unpad( decryptedContents, AES.block_size, "pkcs7" )
  24. def RemoveDrm( self, inputPath: str, outputPath: str, contentKeys: Dict[ str, str ] ) -> None:
  25. with zipfile.ZipFile( inputPath, "r" ) as inputZip:
  26. with zipfile.ZipFile( outputPath, "w", zipfile.ZIP_DEFLATED ) as outputZip:
  27. for filename in inputZip.namelist():
  28. contents = inputZip.read( filename )
  29. contentKeyBase64 = contentKeys.get( filename, None )
  30. if contentKeyBase64 is not None:
  31. contents = self.__DecryptContents( contents, contentKeyBase64 )
  32. outputZip.writestr( filename, contents )