def decrypt(self, content): length = len(content) extra = length & 0x7 if extra > 0: self.warn("content length not a multiple of block size") content += b"\0" * (8 - extra) msdes.deskey(self.bookkey, msdes.DE1) return msdes.des(content)
def _build_drm_storage(self): drmsource = 'Free as in freedom\0'.encode('utf-16-le') self._add_file('/DRMStorage/DRMSource', drmsource) tempkey = self._calculate_deskey([self._meta, drmsource]) msdes.deskey(tempkey, msdes.EN0) self._add_file('/DRMStorage/DRMSealed', msdes.des(b"\0" * 16)) self._bookkey = b'\0' * 8 self._add_file('/DRMStorage/ValidationStream', b'MSReader', 3)
def _build_storage(self): mapping = [ (1, 'MSCompressed', (LZXCOMPRESS_GUID, )), (2, 'EbEncryptDS', (LZXCOMPRESS_GUID, DESENCRYPT_GUID)), (3, 'EbEncryptOnlyDS', (DESENCRYPT_GUID, )), ] for secnum, name, transforms in mapping: root = '::DataSpace/Storage/' + name data = self._sections[secnum].getvalue() cdata, sdata, tdata, rdata = b'', b'', b'', b'' for guid in transforms: tdata = packguid(guid) + tdata sdata = sdata + pack('<Q', len(data)) if guid == DESENCRYPT_GUID: cdata = MSDES_CONTROL + cdata if not data: continue msdes.deskey(self._bookkey, msdes.EN0) pad = 8 - (len(data) & 0x7) if pad != 8: data = data + (b'\0' * pad) data = msdes.des(data) elif guid == LZXCOMPRESS_GUID: cdata = LZXC_CONTROL + cdata if not data: continue unlen = len(data) lzx = Compressor(17) data, rtable = lzx.compress(data, flush=True) rdata = io.BytesIO() rdata.write( pack('<IIIIQQQQ', 3, len(rtable), 8, 0x28, unlen, len(data), 0x8000, 0)) for uncomp, comp in rtable[:-1]: rdata.write(pack('<Q', comp)) rdata = rdata.getvalue() self._add_file(root + '/Content', data) self._add_file(root + '/ControlData', cdata) self._add_file(root + '/SpanInfo', sdata) self._add_file(root + '/Transform/List', tdata) troot = root + '/Transform' for guid in transforms: dname = self._djoin(troot, guid, 'InstanceData') self._add_folder(dname) if guid == LZXCOMPRESS_GUID: dname += '/ResetTable' self._add_file(dname, rdata)
def read_drm(self): self.drmlevel = 0 if '/DRMStorage/Licenses/EUL' in self.entries: self.drmlevel = 5 elif '/DRMStorage/DRMBookplate' in self.entries: self.drmlevel = 3 elif '/DRMStorage/DRMSealed' in self.entries: self.drmlevel = 1 else: return if self.drmlevel < 5: msdes.deskey(self.calculate_deskey(), msdes.DE1) bookkey = msdes.des(self.get_file('/DRMStorage/DRMSealed')) if bookkey[0:1] != b'\0': raise LitError('Unable to decrypt title key!') self.bookkey = bookkey[1:9] else: raise DRMError("Cannot access DRM-protected book")