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 = raw_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: raw_unique_identifier = elem.text break if raw_unique_identifier is not None: idpf_key = raw_unique_identifier idpf_key = re.sub(u'\u0020\u0009\u000d\u000a', u'', idpf_key) idpf_key = hashlib.sha1(idpf_key.encode('utf-8')).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(): tkey = key if alg == ADOBE_OBFUSCATION else idpf_key if not tkey: raise InvalidBook('Failed to find obfuscation key') raw = self.raw_data(font, decode=False) raw = decrypt_font_data(tkey, raw, alg) with self.open(font, 'wb') as f: f.write(raw) self.obfuscated_fonts[font] = (alg, tkey)
def __init__(self, pathtoazw3, log, clone_data=None, tdir=None): if clone_data is not None: super(AZW3Container, self).__init__(None, None, log, clone_data=clone_data) for x in ('pathtoazw3', 'obfuscated_fonts'): setattr(self, x, clone_data[x]) return self.pathtoazw3 = pathtoazw3 if tdir is None: tdir = PersistentTemporaryDirectory('_azw3_container') tdir = os.path.abspath(os.path.realpath(tdir)) self.root = tdir with open(pathtoazw3, 'rb') as stream: raw = stream.read(3) if raw == b'TPZ': raise InvalidMobi( _('This is not a MOBI file. It is a Topaz file.')) try: header = MetadataHeader(stream, default_log) except MobiError: raise InvalidMobi(_('This is not a MOBI file.')) if header.encryption_type != 0: raise DRMError() kf8_type = header.kf8_type if kf8_type is None: raise InvalidMobi( _('This MOBI file does not contain a KF8 format ' 'book. KF8 is the new format from Amazon. calibre can ' 'only edit MOBI files that contain KF8 books. Older ' 'MOBI files without KF8 are not editable.')) if kf8_type == 'joint': raise InvalidMobi( _('This MOBI file contains both KF8 and ' 'older Mobi6 data. calibre can only edit MOBI files ' 'that contain only KF8 data.')) try: opf_path, obfuscated_fonts = fork_job( 'calibre.ebooks.oeb.polish.container', 'do_explode', args=(pathtoazw3, tdir), no_output=True)['result'] except WorkerError as e: log(e.orig_tb) raise InvalidMobi('Failed to explode MOBI') super(AZW3Container, self).__init__(tdir, opf_path, log) self.obfuscated_fonts = { x.replace(os.sep, '/') for x in obfuscated_fonts }
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() cr = em.getparent().xpath( 'descendant::*[local-name()="CipherReference" and @URI]')[0] 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)