def process_encryption(self): fonts = {} enc = self.parsed('META-INF/encryption.xml') for em in enc.xpath( '//*[local-name()="EncryptionMethod" and @Algorithm]'): alg = em.get('Algorithm') if alg not in {ADOBE_OBFUSCATION, IDPF_OBFUSCATION}: raise DRMError() try: cr = em.getparent().xpath( 'descendant::*[local-name()="CipherReference" and @URI]' )[0] except (IndexError, ValueError, KeyError): continue name = self.href_to_name(cr.get('URI')) path = self.name_path_map.get(name, None) if path is not None: fonts[name] = alg if not fonts: return package_id = unique_identifier = idpf_key = None for attrib, val in self.opf.attrib.iteritems(): if attrib.endswith('unique-identifier'): package_id = val break if package_id is not None: for elem in self.opf_xpath('//*[@id=%r]' % package_id): if elem.text: unique_identifier = elem.text.rpartition(':')[-1] break if unique_identifier is not None: idpf_key = hashlib.sha1(unique_identifier).digest() key = None for item in self.opf_xpath('//*[local-name()="metadata"]/*' '[local-name()="identifier"]'): scheme = None for xkey in item.attrib.keys(): if xkey.endswith('scheme'): scheme = item.get(xkey) if (scheme and scheme.lower() == 'uuid') or \ (item.text and item.text.startswith('urn:uuid:')): try: key = bytes(item.text).rpartition(':')[-1] key = uuid.UUID(key).bytes except: self.log.exception('Failed to parse obfuscation key') key = None for font, alg in fonts.iteritems(): path = self.name_path_map[font] tkey = key if alg == ADOBE_OBFUSCATION else idpf_key if not tkey: raise InvalidBook('Failed to find obfuscation key') decrypt_font(tkey, path, alg) self.obfuscated_fonts[font] = (alg, tkey)
def process_encryption(self): fonts = {} enc = self.parsed('META-INF/encryption.xml') for em in enc.xpath('//*[local-name()="EncryptionMethod" and @Algorithm]'): alg = em.get('Algorithm') if alg not in {ADOBE_OBFUSCATION, IDPF_OBFUSCATION}: raise DRMError() try: cr = em.getparent().xpath('descendant::*[local-name()="CipherReference" and @URI]')[0] except (IndexError, ValueError, KeyError): continue name = self.href_to_name(cr.get('URI')) path = self.name_path_map.get(name, None) if path is not None: fonts[name] = alg if not fonts: return package_id = unique_identifier = idpf_key = None for attrib, val in self.opf.attrib.iteritems(): if attrib.endswith('unique-identifier'): package_id = val break if package_id is not None: for elem in self.opf_xpath('//*[@id=%r]'%package_id): if elem.text: unique_identifier = elem.text.rpartition(':')[-1] break if unique_identifier is not None: idpf_key = hashlib.sha1(unique_identifier).digest() key = None for item in self.opf_xpath('//*[local-name()="metadata"]/*' '[local-name()="identifier"]'): scheme = None for xkey in item.attrib.keys(): if xkey.endswith('scheme'): scheme = item.get(xkey) if (scheme and scheme.lower() == 'uuid') or \ (item.text and item.text.startswith('urn:uuid:')): try: key = bytes(item.text).rpartition(':')[-1] key = uuid.UUID(key).bytes except: self.log.exception('Failed to parse obfuscation key') key = None for font, alg in fonts.iteritems(): path = self.name_path_map[font] tkey = key if alg == ADOBE_OBFUSCATION else idpf_key if not tkey: raise InvalidBook('Failed to find obfuscation key') decrypt_font(tkey, path, alg) self.obfuscated_fonts[font] = (alg, tkey)
def commit(self, outpath=None, keep_parsed=False): super(EpubContainer, self).commit(keep_parsed=keep_parsed) for name in self.obfuscated_fonts: if name not in self.name_path_map: continue alg, key = self.obfuscated_fonts[name] # Decrypting and encrypting are the same operation (XOR with key) decrypt_font(key, self.name_path_map[name], alg) if outpath is None: outpath = self.pathtoepub from calibre.ebooks.tweak import zip_rebuilder with open(join(self.root, 'mimetype'), 'wb') as f: f.write(guess_type('a.epub')) zip_rebuilder(self.root, outpath)