daily pastebin goal
13%
SHARE
TWEET

Untitled

a guest Aug 10th, 2011 1,861 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #! /usr/bin/python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # ineptepub.pyw, version 5.2
  5. # Copyright © 2009-2010 i♥cabbages
  6.  
  7. # Released under the terms of the GNU General Public Licence, version 3 or
  8. # later.  <http://www.gnu.org/licenses/>
  9.  
  10. # Windows users: Before running this program, you must first install Python 2.6
  11. #   from <http://www.python.org/download/> and PyCrypto from
  12. #   <http://www.voidspace.org.uk/python/modules.shtml#pycrypto> (make sure to
  13. #   install the version for Python 2.6).  Save this script file as
  14. #   ineptepub.pyw and double-click on it to run it.
  15. #
  16. # Mac OS X users: Save this script file as ineptepub.pyw.  You can run this
  17. #   program from the command line (pythonw ineptepub.pyw) or by double-clicking
  18. #   it when it has been associated with PythonLauncher.
  19.  
  20. # Revision history:
  21. #   1 - Initial release
  22. #   2 - Rename to INEPT, fix exit code
  23. #   5 - Version bump to avoid (?) confusion;
  24. #       Improve OS X support by using OpenSSL when available
  25. #   5.1 - Improve OpenSSL error checking
  26. #   5.2 - Fix ctypes error causing segfaults on some systems
  27.  
  28. """
  29. Decrypt Adobe ADEPT-encrypted EPUB books.
  30. """
  31.  
  32. from __future__ import with_statement
  33.  
  34. __license__ = 'GPL v3'
  35.  
  36. import sys
  37. import os
  38. import zlib
  39. import zipfile
  40. from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
  41. from contextlib import closing
  42. import xml.etree.ElementTree as etree
  43. import Tkinter
  44. import Tkconstants
  45. import tkFileDialog
  46. import tkMessageBox
  47.  
  48. class ADEPTError(Exception):
  49.     pass
  50.  
  51. def _load_crypto_libcrypto():
  52.     from ctypes import CDLL, POINTER, c_void_p, c_char_p, c_int, c_long, \
  53.         Structure, c_ulong, create_string_buffer, cast
  54.     from ctypes.util import find_library
  55.  
  56.     libcrypto = find_library('crypto')
  57.     if libcrypto is None:
  58.         raise ADEPTError('libcrypto not found')
  59.     libcrypto = CDLL(libcrypto)
  60.  
  61.     RSA_NO_PADDING = 3
  62.     AES_MAXNR = 14
  63.    
  64.     c_char_pp = POINTER(c_char_p)
  65.     c_int_p = POINTER(c_int)
  66.  
  67.     class RSA(Structure):
  68.         pass
  69.     RSA_p = POINTER(RSA)
  70.    
  71.     class AES_KEY(Structure):
  72.         _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))),
  73.                     ('rounds', c_int)]
  74.     AES_KEY_p = POINTER(AES_KEY)
  75.    
  76.     def F(restype, name, argtypes):
  77.         func = getattr(libcrypto, name)
  78.         func.restype = restype
  79.         func.argtypes = argtypes
  80.         return func
  81.    
  82.     d2i_RSAPrivateKey = F(RSA_p, 'd2i_RSAPrivateKey',
  83.                           [RSA_p, c_char_pp, c_long])
  84.     RSA_size = F(c_int, 'RSA_size', [RSA_p])
  85.     RSA_private_decrypt = F(c_int, 'RSA_private_decrypt',
  86.                             [c_int, c_char_p, c_char_p, RSA_p, c_int])
  87.     RSA_free = F(None, 'RSA_free', [RSA_p])
  88.     AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',
  89.                             [c_char_p, c_int, AES_KEY_p])
  90.     AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',
  91.                         [c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,
  92.                          c_int])
  93.    
  94.     class RSA(object):
  95.         def __init__(self, der):
  96.             buf = create_string_buffer(der)
  97.             pp = c_char_pp(cast(buf, c_char_p))
  98.             rsa = self._rsa = d2i_RSAPrivateKey(None, pp, len(der))
  99.             if rsa is None:
  100.                 raise ADEPTError('Error parsing ADEPT user key DER')
  101.        
  102.         def decrypt(self, from_):
  103.             rsa = self._rsa
  104.             to = create_string_buffer(RSA_size(rsa))
  105.             dlen = RSA_private_decrypt(len(from_), from_, to, rsa,
  106.                                        RSA_NO_PADDING)
  107.             if dlen < 0:
  108.                 raise ADEPTError('RSA decryption failed')
  109.             return to[:dlen]
  110.    
  111.         def __del__(self):
  112.             if self._rsa is not None:
  113.                 RSA_free(self._rsa)
  114.                 self._rsa = None
  115.  
  116.     class AES(object):
  117.         def __init__(self, userkey):
  118.             self._blocksize = len(userkey)
  119.             key = self._key = AES_KEY()
  120.             rv = AES_set_decrypt_key(userkey, len(userkey) * 8, key)
  121.             if rv < 0:
  122.                 raise ADEPTError('Failed to initialize AES key')
  123.    
  124.         def decrypt(self, data):
  125.             out = create_string_buffer(len(data))
  126.             iv = ("\x00" * self._blocksize)
  127.             rv = AES_cbc_encrypt(data, out, len(data), self._key, iv, 0)
  128.             if rv == 0:
  129.                 raise ADEPTError('AES decryption failed')
  130.             return out.raw
  131.  
  132.     return (AES, RSA)
  133.  
  134. def _load_crypto_pycrypto():
  135.     from Crypto.Cipher import AES as _AES
  136.     from Crypto.PublicKey import RSA as _RSA
  137.  
  138.     # ASN.1 parsing code from tlslite
  139.     class ASN1Error(Exception):
  140.         pass
  141.    
  142.     class ASN1Parser(object):
  143.         class Parser(object):
  144.             def __init__(self, bytes):
  145.                 self.bytes = bytes
  146.                 self.index = 0
  147.    
  148.             def get(self, length):
  149.                 if self.index + length > len(self.bytes):
  150.                     raise ASN1Error("Error decoding ASN.1")
  151.                 x = 0
  152.                 for count in range(length):
  153.                     x <<= 8
  154.                     x |= self.bytes[self.index]
  155.                     self.index += 1
  156.                 return x
  157.    
  158.             def getFixBytes(self, lengthBytes):
  159.                 bytes = self.bytes[self.index : self.index+lengthBytes]
  160.                 self.index += lengthBytes
  161.                 return bytes
  162.    
  163.             def getVarBytes(self, lengthLength):
  164.                 lengthBytes = self.get(lengthLength)
  165.                 return self.getFixBytes(lengthBytes)
  166.    
  167.             def getFixList(self, length, lengthList):
  168.                 l = [0] * lengthList
  169.                 for x in range(lengthList):
  170.                     l[x] = self.get(length)
  171.                 return l
  172.    
  173.             def getVarList(self, length, lengthLength):
  174.                 lengthList = self.get(lengthLength)
  175.                 if lengthList % length != 0:
  176.                     raise ASN1Error("Error decoding ASN.1")
  177.                 lengthList = int(lengthList/length)
  178.                 l = [0] * lengthList
  179.                 for x in range(lengthList):
  180.                     l[x] = self.get(length)
  181.                 return l
  182.    
  183.             def startLengthCheck(self, lengthLength):
  184.                 self.lengthCheck = self.get(lengthLength)
  185.                 self.indexCheck = self.index
  186.    
  187.             def setLengthCheck(self, length):
  188.                 self.lengthCheck = length
  189.                 self.indexCheck = self.index
  190.    
  191.             def stopLengthCheck(self):
  192.                 if (self.index - self.indexCheck) != self.lengthCheck:
  193.                     raise ASN1Error("Error decoding ASN.1")
  194.    
  195.             def atLengthCheck(self):
  196.                 if (self.index - self.indexCheck) < self.lengthCheck:
  197.                     return False
  198.                 elif (self.index - self.indexCheck) == self.lengthCheck:
  199.                     return True
  200.                 else:
  201.                     raise ASN1Error("Error decoding ASN.1")
  202.    
  203.         def __init__(self, bytes):
  204.             p = self.Parser(bytes)
  205.             p.get(1)
  206.             self.length = self._getASN1Length(p)
  207.             self.value = p.getFixBytes(self.length)
  208.    
  209.         def getChild(self, which):
  210.             p = self.Parser(self.value)
  211.             for x in range(which+1):
  212.                 markIndex = p.index
  213.                 p.get(1)
  214.                 length = self._getASN1Length(p)
  215.                 p.getFixBytes(length)
  216.             return ASN1Parser(p.bytes[markIndex:p.index])
  217.    
  218.         def _getASN1Length(self, p):
  219.             firstLength = p.get(1)
  220.             if firstLength<=127:
  221.                 return firstLength
  222.             else:
  223.                 lengthLength = firstLength & 0x7F
  224.                 return p.get(lengthLength)
  225.  
  226.     class AES(object):
  227.         def __init__(self, key):
  228.             self._aes = _AES.new(key, _AES.MODE_CBC)
  229.  
  230.         def decrypt(self, data):
  231.             return self._aes.decrypt(data)
  232.  
  233.     class RSA(object):
  234.         def __init__(self, der):
  235.             key = ASN1Parser([ord(x) for x in der])
  236.             key = [key.getChild(x).value for x in xrange(1, 4)]
  237.             key = [self.bytesToNumber(v) for v in key]
  238.             self._rsa = _RSA.construct(key)
  239.  
  240.         def bytesToNumber(self, bytes):
  241.             total = 0L
  242.             for byte in bytes:
  243.                 total = (total << 8) + byte
  244.             return total
  245.    
  246.         def decrypt(self, data):
  247.             return self._rsa.decrypt(data)
  248.  
  249.     return (AES, RSA)
  250.  
  251. def _load_crypto():
  252.     AES = RSA = None
  253.     for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto):
  254.         try:
  255.             AES, RSA = loader()
  256.             break
  257.         except (ImportError, ADEPTError):
  258.             pass
  259.     return (AES, RSA)
  260. AES, RSA = _load_crypto()
  261.  
  262. META_NAMES = ('mimetype', 'META-INF/rights.xml', 'META-INF/encryption.xml')
  263. NSMAP = {'adept': 'http://ns.adobe.com/adept',
  264.          'enc': 'http://www.w3.org/2001/04/xmlenc#'}
  265.  
  266. class ZipInfo(zipfile.ZipInfo):
  267.     def __init__(self, *args, **kwargs):
  268.         if 'compress_type' in kwargs:
  269.             compress_type = kwargs.pop('compress_type')
  270.         super(ZipInfo, self).__init__(*args, **kwargs)
  271.         self.compress_type = compress_type
  272.  
  273. class Decryptor(object):
  274.     def __init__(self, bookkey, encryption):
  275.         enc = lambda tag: '{%s}%s' % (NSMAP['enc'], tag)
  276.         self._aes = AES(bookkey)
  277.         encryption = etree.fromstring(encryption)
  278.         self._encrypted = encrypted = set()
  279.         expr = './%s/%s/%s' % (enc('EncryptedData'), enc('CipherData'),
  280.                                enc('CipherReference'))
  281.         for elem in encryption.findall(expr):
  282.             path = elem.get('URI', None)
  283.             if path is not None:
  284.                 encrypted.add(path)
  285.  
  286.     def decompress(self, bytes):
  287.         dc = zlib.decompressobj(-15)
  288.         bytes = dc.decompress(bytes)
  289.         ex = dc.decompress('Z') + dc.flush()
  290.         if ex:
  291.             bytes = bytes + ex
  292.         return bytes
  293.  
  294.     def decrypt(self, path, data):
  295.         if path in self._encrypted:
  296.             data = self._aes.decrypt(data)[16:]
  297.             data = data[:-ord(data[-1])]
  298.             data = self.decompress(data)
  299.         return data
  300.  
  301. def cli_main(argv=sys.argv):
  302.     progname = os.path.basename(argv[0])
  303.     if AES is None:
  304.         print "%s: This script requires OpenSSL or PyCrypto, which must be" \
  305.               " installed separately.  Read the top-of-script comment for" \
  306.               " details." % (progname,)
  307.         return 1
  308.     if len(argv) != 4:
  309.         print "usage: %s KEYFILE INBOOK OUTBOOK" % (progname,)
  310.         return 1
  311.     keypath, inpath, outpath = argv[1:]
  312.     with open(keypath, 'rb') as f:
  313.         keyder = f.read()
  314.     rsa = RSA(keyder)
  315.     with closing(ZipFile(open(inpath, 'rb'))) as inf:
  316.         namelist = set(inf.namelist())
  317.         if 'META-INF/rights.xml' not in namelist or \
  318.            'META-INF/encryption.xml' not in namelist:
  319.             raise ADEPTError('%s: not an ADEPT EPUB' % (inpath,))
  320.         for name in META_NAMES:
  321.             namelist.remove(name)
  322.         rights = etree.fromstring(inf.read('META-INF/rights.xml'))
  323.         adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
  324.         expr = './/%s' % (adept('encryptedKey'),)
  325.         bookkey = ''.join(rights.findtext(expr))
  326.         bookkey = rsa.decrypt(bookkey.decode('base64'))
  327.         # Padded as per RSAES-PKCS1-v1_5
  328.         if bookkey[-17] != '\x00':
  329.             raise ADEPTError('problem decrypting session key')
  330.         encryption = inf.read('META-INF/encryption.xml')
  331.         decryptor = Decryptor(bookkey[-16:], encryption)
  332.         kwds = dict(compression=ZIP_DEFLATED, allowZip64=False)
  333.         with closing(ZipFile(open(outpath, 'wb'), 'w', **kwds)) as outf:
  334.             zi = ZipInfo('mimetype', compress_type=ZIP_STORED)
  335.             outf.writestr(zi, inf.read('mimetype'))
  336.             for path in namelist:
  337.                 data = inf.read(path)
  338.                 outf.writestr(path, decryptor.decrypt(path, data))
  339.     return 0
  340.  
  341. class DecryptionDialog(Tkinter.Frame):
  342.     def __init__(self, root):
  343.         Tkinter.Frame.__init__(self, root, border=5)
  344.         self.status = Tkinter.Label(self, text='Select files for decryption')
  345.         self.status.pack(fill=Tkconstants.X, expand=1)
  346.         body = Tkinter.Frame(self)
  347.         body.pack(fill=Tkconstants.X, expand=1)
  348.         sticky = Tkconstants.E + Tkconstants.W
  349.         body.grid_columnconfigure(1, weight=2)
  350.         Tkinter.Label(body, text='Key file').grid(row=0)
  351.         self.keypath = Tkinter.Entry(body, width=30)
  352.         self.keypath.grid(row=0, column=1, sticky=sticky)
  353.         if os.path.exists('adeptkey.der'):
  354.             self.keypath.insert(0, 'adeptkey.der')
  355.         button = Tkinter.Button(body, text="...", command=self.get_keypath)
  356.         button.grid(row=0, column=2)
  357.         Tkinter.Label(body, text='Input file').grid(row=1)
  358.         self.inpath = Tkinter.Entry(body, width=30)
  359.         self.inpath.grid(row=1, column=1, sticky=sticky)
  360.         button = Tkinter.Button(body, text="...", command=self.get_inpath)
  361.         button.grid(row=1, column=2)
  362.         Tkinter.Label(body, text='Output file').grid(row=2)
  363.         self.outpath = Tkinter.Entry(body, width=30)
  364.         self.outpath.grid(row=2, column=1, sticky=sticky)
  365.         button = Tkinter.Button(body, text="...", command=self.get_outpath)
  366.         button.grid(row=2, column=2)
  367.         buttons = Tkinter.Frame(self)
  368.         buttons.pack()
  369.         botton = Tkinter.Button(
  370.             buttons, text="Decrypt", width=10, command=self.decrypt)
  371.         botton.pack(side=Tkconstants.LEFT)
  372.         Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
  373.         button = Tkinter.Button(
  374.             buttons, text="Quit", width=10, command=self.quit)
  375.         button.pack(side=Tkconstants.RIGHT)
  376.  
  377.     def get_keypath(self):
  378.         keypath = tkFileDialog.askopenfilename(
  379.             parent=None, title='Select ADEPT key file',
  380.             defaultextension='.der', filetypes=[('DER-encoded files', '.der'),
  381.                                                 ('All Files', '.*')])
  382.         if keypath:
  383.             keypath = os.path.normpath(keypath)
  384.             self.keypath.delete(0, Tkconstants.END)
  385.             self.keypath.insert(0, keypath)
  386.         return
  387.  
  388.     def get_inpath(self):
  389.         inpath = tkFileDialog.askopenfilename(
  390.             parent=None, title='Select ADEPT-encrypted EPUB file to decrypt',
  391.             defaultextension='.epub', filetypes=[('EPUB files', '.epub'),
  392.                                                  ('All files', '.*')])
  393.         if inpath:
  394.             inpath = os.path.normpath(inpath)
  395.             self.inpath.delete(0, Tkconstants.END)
  396.             self.inpath.insert(0, inpath)
  397.         return
  398.  
  399.     def get_outpath(self):
  400.         outpath = tkFileDialog.asksaveasfilename(
  401.             parent=None, title='Select unencrypted EPUB file to produce',
  402.             defaultextension='.epub', filetypes=[('EPUB files', '.epub'),
  403.                                                  ('All files', '.*')])
  404.         if outpath:
  405.             outpath = os.path.normpath(outpath)
  406.             self.outpath.delete(0, Tkconstants.END)
  407.             self.outpath.insert(0, outpath)
  408.         return
  409.  
  410.     def decrypt(self):
  411.         keypath = self.keypath.get()
  412.         inpath = self.inpath.get()
  413.         outpath = self.outpath.get()
  414.         if not keypath or not os.path.exists(keypath):
  415.             self.status['text'] = 'Specified key file does not exist'
  416.             return
  417.         if not inpath or not os.path.exists(inpath):
  418.             self.status['text'] = 'Specified input file does not exist'
  419.             return
  420.         if not outpath:
  421.             self.status['text'] = 'Output file not specified'
  422.             return
  423.         if inpath == outpath:
  424.             self.status['text'] = 'Must have different input and output files'
  425.             return
  426.         argv = [sys.argv[0], keypath, inpath, outpath]
  427.         self.status['text'] = 'Decrypting...'
  428.         try:
  429.             cli_main(argv)
  430.         except Exception, e:
  431.             self.status['text'] = 'Error: ' + str(e)
  432.             return
  433.         self.status['text'] = 'File successfully decrypted'
  434.  
  435. def gui_main():
  436.     root = Tkinter.Tk()
  437.     if AES is None:
  438.         root.withdraw()
  439.         tkMessageBox.showerror(
  440.             "INEPT EPUB Decrypter",
  441.             "This script requires OpenSSL or PyCrypto, which must be"
  442.             " installed separately.  Read the top-of-script comment for"
  443.             " details.")
  444.         return 1
  445.     root.title('INEPT EPUB Decrypter')
  446.     root.resizable(True, False)
  447.     root.minsize(300, 0)
  448.     DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
  449.     root.mainloop()
  450.     return 0
  451.  
  452. if __name__ == '__main__':
  453.     if len(sys.argv) > 1:
  454.         sys.exit(cli_main())
  455.     sys.exit(gui_main())
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top