2025-03-03, 14:40
  #37
Medlem
Citat:
Ursprungligen postat av filifjonken
Det är faktiskt ett EU-direktiv. Jättemärkligt. Epub-formatet anses inte "tillgängligt". Det bror på att det inte funkar med text-to-speach. Vilktet i sig är märkligt då det bara är zippade HTML-filer. Vilket format man kommer att använda istället vet jag inte. Adobe DRM försvinner, istället kommer Readium LCP att användas.

Det här är katstrof för oss som brukar låna e-böcker och läsa på läsplatta. Men kommer alltså bara att kunna ladda ner med Biblioappen. Och vem vill läsa romaner på mobben? Om man lyckas lokalisera boken och föra över den till läsplatten kommer den alltså att var krypterad där.

Det finns grejer som knäcker Readium, men det krävs nån slags "key". Tydligen använder vissa amerikanska sajter som säljer böcker Readium och då får men tydligen denna nyckel. Det lär man inte få av biblioteket. Man kan installera Biblio på lvissa läsplattor som har Android om OS, men jag misstänker att boken ändå kommer att öppenas i Biblio-appen och den är ingen fröjd för ögat. Man kan inte ens ändra textstorlek. Zoomar man kommer texten utanför skärmen.

Vet inte ens om det finns riktiga läsplattor som kör Android, d.v.s. har en ickestrålande skärm med elektroniskt bläck. Misstänker att bibliotekens information inte kan skilja på läsplattor och små surfplattor.

Så för att göra det tillgängligt för ett fåtal blir det alltså oerhört otillgängligt för ett flertal.

Ur "tillgänglighetssynpunkt" borde det här inte vara något problem för epub3-filer. Och Biblio/WeDoBooks använder ju också epub-filer - bara att de är låsta till själva appen/hemsidan med DRM. Det är alltså rent trams att nedladdning inte skulle gå att implementera. Det finns ju nätbokhandlar som säljer epub-filer med LCP-skydd, vilka går att ladda ned och avkoda med Thorium med den nyckel som man får med boken.

Jag har e-läsare med Android (se Onyx Boox, Bigme). Problemet är att själva appen är undermålig: knappt något går att justera, det går inte att avaktivera animationen när man byter sida och det går heller inte att byta sida med volymknapparna, etc.

Citat:
Ursprungligen postat av loke-mstr
Jag har fipplat lite på det här ikväll. På Biblios hemsida så kan man också läsa e-böcker, om man öppnar DevTools->Network i Chrome när man öppnar en lånad bok för läsning så kan man få tag på den krypterade epub-filen genom att filtrera på "LoanManager-request_ebook_link" och kolla på svaret. Svaret är ett Json-dokument som innehåller en länk och en nyckel, klistra in den länken så borde den krypterade epub-filen laddas ner. Jag tror att filen är krypterad med Readium LCP men jag kunde inte dekryptera den med några av verktygen som jag hittade med nyckeln från json-dokumentet så jag gick bet där.

Eftersom jag bara var ute efter en bok den här gången så tog jag den långa vägen och filtrerade network på Doc och högerklicka->spara varje blob som html-fil (plus tre css-filer)och la ihop dem till en epub senare. Det verkade som att varje blob var ett kapitel (säkert att varje blob var en fil i epub-boken) så det var inte så jobbigt. Definitivt inte en lösning om man tänker låna ofta men helt okej om det är en enskild bok man är ute efter.

Detta var kanske inte så upplysande men jag ville bara dokumentera min metod och kanske hjälpa som diskussionsunderlag. Eftersom de skickar en krypterad epub till webbläsaren och sedan visar innehållet så antar jag att dekrypteringen sker i webbläsaren och inte på deras server och då borde väl nyckeln finnas synlig någonstans men det är en svårighetsgrad högre än jag klarar.

Jag har installerat en version av DeDRM med LCP Readium till Calibre, samt en plugin med stöd för LCP-filer. Då fungerar det att ladda ned och avkryptera testfilerna här: https://www.edrlab.org/readium-lcp/t...liant-devices/.

Det som krävs är att man skriver in rätt LCP-kod i DeDRMs inställningar. I det här fallet är koden “edrlab rocks”, vilket också står på hemsidan.

För att testa om det fungerar med Biblio skulle jag behöva komma åt koden till boken (själva boken är lätt att hitta i Androids filsystem).

Men det är inte troligt att det fungerar. DeDRM stödjer bara den äldre varianten av LCPs DRM. Jag köpte en bok från en internetbokhandel och kopierade in nyckeln i DeDRM - boken laddades ned, men Calibre lyckades inte ta bort skyddet.

Däremot fungerade allt med Thorium, efter att jag kopierat in nyckeln där.
Citera
2025-03-07, 13:28
  #38
Medlem
Kan tipsa om oceanofpdf för att ladda ner e-böcker.
Citera
2025-03-09, 07:09
  #39
Medlem
Calibres gamla DeDRM-plugin som fortfarande stöder LCP lyckades få bort skyddet från Bokus LCP-skyddade e-böcker.

Men jag vet inte hur man hittar rätt "passphrase", dvs lösenordet till boken, i biblio.

Någon som har någon idé?
Citera
2025-03-13, 19:15
  #40
Medlem
Verkar som att det blir en liten frist med några månaders extra nedladdningsmöjligheter för de bibliotek som skriver upp sig: https://info.biblio.app/sv/news/adob...der-utveckling
Citera
2025-05-15, 09:34
  #41
Medlem
Några kända bibliotek som valt detta?
Citera
2025-05-30, 16:43
  #42
Medlem
Biblio

Jag använder mig av mitmproxy och en modifierad main.js som spottar ut nyckeln i konsolen.

"this.userKey" innehåller en AES-nyckel som kan används för att avkryptera innehållet i .epub-filen som Biblio tillhandahåller.

Kanske någon som är lite vassare kan fixa ett Tampermoney/Greasemonkey-skript som spottar ut koden och fixar nerladdning av .epub-filen?

När jag har .epub-fil och kod så kör jag allt igenom python-skriptet (som ChatGPT fixade).

Kod:
    const K$e = class BBEncryptionMethod {
      constructor(e, t) {
        this.key = undefined;
        this.uid = undefined;
        this.userKey = null;
        this.userKeyPromise = undefined;
        this.key = e;
        this.uid = t;
        this.userKeyPromise = this.key === "" ? Promise.resolve(W$e.alloc(0)) : Q$e(this.key, this.uid);
      }
      async decrypt(e, t, n) {
        try {
          var r;
          this.userKey ||= await this.userKeyPromise;
          const t = "aes-128-cbc";
          const i = new Uint8Array(e.slice(0, 16));
          const o = W$e.from(e.slice(16));
          const a = (0, LG._7)(t, this.userKey, i);
          const s = W$e.concat([a.update(o), a.final()]);
          if (((r = n.encryptionProperties) == null ? undefined : r.compressionMethod) === 8) {
            return H$e.inflateRaw(s);
          }
          return s;
        } catch ($H) {
          zG("decrypt err", $H);
          throw $H;
        }
      }

Kod:
import os
import zipfile
import shutil
from lxml import etree
from Crypto.Cipher import AES
import zlib

# Namespace map for Encryption.xml
NAMESPACES = {
    'enc': 'http://www.w3.org/2001/04/xmlenc#',
    'ds': 'http://www.w3.org/2000/09/xmldsig#',
    'comp': 'http://www.idpf.org/2016/encryption#compression'
}

def unzip_epub(epub_path, extract_dir):
    print(f"[+] Extracting EPUB '{epub_path}' to '{extract_dir}'")
    with zipfile.ZipFile(epub_path, 'r') as zip_ref:
        zip_ref.extractall(extract_dir)
    print("[+] Extraction done")

def zip_epub(folder_path, output_path):
    print(f"[+] Creating EPUB '{output_path}' from '{folder_path}'")
    with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
        for root, _, files in os.walk(folder_path):
            for file in files:
                full_path = os.path.join(root, file)
                arcname = os.path.relpath(full_path, folder_path)
                zipf.write(full_path, arcname)
    print("[+] EPUB repackaged")

def parse_encryption_xml(enc_xml_path):
    print(f"[+] Parsing Encryption.xml at '{enc_xml_path}'")
    tree = etree.parse(enc_xml_path)
    root = tree.getroot()
    encrypted_files = []

    for enc_data in root.findall('.//enc:EncryptedData', namespaces=NAMESPACES):
        cipher_ref = enc_data.find('.//enc:CipherReference', namespaces=NAMESPACES)
        if cipher_ref is None:
            print("[!] Found EncryptedData without CipherReference, skipping")
            continue
        uri = cipher_ref.get('URI')
        
        compression_elem = enc_data.find('.//enc:EncryptionProperty/comp:Compression', namespaces=NAMESPACES)
        compression = None
        original_length = None
        if compression_elem is not None:
            compression = compression_elem.get('Method')
            original_length = compression_elem.get('OriginalLength')

        encrypted_files.append({
            'URI': uri,
            'compression_method': compression,
            'original_length': int(original_length) if original_length else None
        })
        print(f"  -> Encrypted file: {uri}, compression: {compression}, original length: {original_length}")
    return encrypted_files

def remove_pkcs7_padding(data):
    pad_len = data[-1]
    if pad_len < 1 or pad_len > 16:
        raise ValueError(f"Invalid padding length: {pad_len}")
    if data[-pad_len:] != bytes([pad_len]) * pad_len:
        raise ValueError(f"Invalid PKCS7 padding bytes: {data[-pad_len:]}")
    return data[:-pad_len]

def decrypt_aes128_cbc(data, key):
    iv = data[:16]
    ciphertext = data[16:]
    print(f"[*] Using IV: {iv.hex()}")
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted = cipher.decrypt(ciphertext)
    try:
        unpadded = remove_pkcs7_padding(decrypted)
    except ValueError as e:
        print(f"    [!] Padding error: {e}")
        print("    [!] Returning raw decrypted data without padding removal")
        return decrypted
    return unpadded

def hexdump(data, length=20):
    return ' '.join(f'{b:02x}' for b in data[:length])

def decompress_if_needed(data, method):
    import zlib
    if method == "8":
        print(f"[*] First 20 bytes of decrypted data (hex): {hexdump(data)}")
        try:
            # Try decompress with zlib wrapper
            return zlib.decompress(data)
        except zlib.error:
            try:
                # Try raw deflate (wbits=-15)
                decompressed = zlib.decompress(data, wbits=-15)
                print("    [+] Successfully decompressed raw deflate data")
                return decompressed
            except zlib.error as e:
                print(f"    [!] Both zlib and raw deflate decompression failed: {e}")
                print("    [!] Returning raw decrypted data")
                return data
    else:
        print(f"[*] No or unknown compression method '{method}', skipping decompression")
        return data

def main(epub_path, secret_key, output_epub):
    workdir = 'tmp_epub_extracted'

    if os.path.exists(workdir):
        print(f"[!] Removing existing work directory '{workdir}'")
        shutil.rmtree(workdir)
    os.makedirs(workdir)

    unzip_epub(epub_path, workdir)

    encryption_xml_path = os.path.join(workdir, 'META-INF', 'encryption.xml')
    if not os.path.exists(encryption_xml_path):
        print("[!] No encryption.xml found, nothing to decrypt.")
        return

    encrypted_files = parse_encryption_xml(encryption_xml_path)

    for enc_file in encrypted_files:
        file_path = os.path.join(workdir, *enc_file['URI'].split('/'))
        if not os.path.exists(file_path):
            print(f"[!] Encrypted file '{enc_file['URI']}' not found, skipping")
            continue

        print(f"[+] Decrypting '{enc_file['URI']}'")
        with open(file_path, 'rb') as f:
            encrypted_data = f.read()

        print(f"[*] Encrypted data length: {len(encrypted_data)} bytes")
        decrypted_data = decrypt_aes128_cbc(encrypted_data, secret_key)
        print(f"[*] Decrypted data length: {len(decrypted_data)} bytes")
        
        decompressed_data = decompress_if_needed(decrypted_data, enc_file['compression_method'])
        print(f"[*] Final data length after decompression (if any): {len(decompressed_data)} bytes")

        with open(file_path, 'wb') as f:
            f.write(decompressed_data)
        print(f"    [+] Replaced encrypted file with decrypted content")

        enc_xml_path = os.path.join(workdir, 'META-INF', 'encryption.xml')
        if os.path.exists(enc_xml_path):
            print("[+] Removing encryption.xml to mark EPUB as decrypted")
            os.remove(enc_xml_path)

    zip_epub(workdir, output_epub)
    print(f"[+] Decrypted EPUB saved as '{output_epub}'")

if __name__ == "__main__":
    import sys
    if len(sys.argv) != 4:
        print("Usage: python decrypt_epub.py <encrypted.epub> <16-byte-hex-key> <output.epub>")
        print("Example key: 00112233445566778899aabbccddeeff")
        sys.exit(1)

    epub_path = sys.argv[1]
    key_hex = sys.argv[2]
    output_epub = sys.argv[3]

    key_bytes = bytes.fromhex(key_hex)
    if len(key_bytes) != 16:
        print("[!] Key must be 16 bytes (32 hex chars) for AES-128.")
        sys.exit(1)

    main(epub_path, key_bytes, output_epub)
__________________
Senast redigerad av [BPS] 2025-05-30 kl. 16:46.
Citera
2025-05-31, 13:32
  #43
Medlem
Citat:
Ursprungligen postat av [BPS]'
.split('/'))
if not os.path.exists(file_path):
print(f"[!] Encrypted file '{enc_file['URI']}' not found, skipping")
continue

print(f"[+] Decrypting '{enc_file['URI']}'")
with open(file_path, 'rb') as f:
encrypted_data = f.read()

print(f"[*] Encrypted data length: {len(encrypted_data)} bytes")
decrypted_data = decrypt_aes128_cbc(encrypted_data, secret_key)
print(f"[*] Decrypted data length: {len(decrypted_data)} bytes")

decompressed_data = decompress_if_needed(decrypted_data, enc_file['compression_method'])
print(f"[*] Final data length after decompression (if any): {len(decompressed_data)} bytes")

with open(file_path, 'wb') as f:
f.write(decompressed_data)
print(f" [+] Replaced encrypted file with decrypted content")

enc_xml_path = os.path.join(workdir, 'META-INF', 'encryption.xml')
if os.path.exists(enc_xml_path):
print("[+] Removing encryption.xml to mark EPUB as decrypted")
os.remove(enc_xml_path)

zip_epub(workdir, output_epub)
print(f"[+] Decrypted EPUB saved as '{output_epub}'")

if __name__ == "__main__":
import sys
if len(sys.argv) != 4:
print("Usage: python decrypt_epub.py <encrypted.epub> <16-byte-hex-key> <output.epub>")
print("Example key: 00112233445566778899aabbccddeeff")
sys.exit(1)

epub_path = sys.argv[1]
key_hex = sys.argv[2]
output_epub = sys.argv[3]

key_bytes = bytes.fromhex(key_hex)
if len(key_bytes) != 16:
print("[!] Key must be 16 bytes (32 hex chars) for AES-128.")
sys.exit(1)

main(epub_path, key_bytes, output_epub)
[/code]
Wow, vilket framsteg!
Skulle du kunna förklara utförligare hur du gjorde för att modifiera main.js och extraherade nyckeln? Jag installerade mitmproxy och lyckades läsa av trafiken men jag sökte på main.js och hittade inget, är också osäker på var i koden man ska lägga in din kod (som också saknar en avslutande häftklammer)
Citera
2025-06-01, 15:08
  #44
Medlem
https://github.com/skunkmailen/BEPUB

"Mitmproxy" behövs inte längre.

Tampermonkey används för att få ut all nödvändig info från Biblio.
  • Länk till EPUB-filen.
  • User ID - ditt unika användar ID.
  • Key - nyckel för att låsa upp EPUB-filen.
Denna info kopieras därefter in i Python-skriptet som används för att skapa en .EPUB-fil utan DRM.

Viktigt att tänka på:
Infon du får från Tampermonkey är knuten till din användare, så posta inte infon här eller någon annanstans.

Citat:
Ursprungligen postat av loke-mstr
Wow, vilket framsteg!
Skulle du kunna förklara utförligare hur du gjorde för att modifiera main.js och extraherade nyckeln? Jag installerade mitmproxy och lyckades läsa av trafiken men jag sökte på main.js och hittade inget, är också osäker på var i koden man ska lägga in din kod (som också saknar en avslutande häftklammer)
Citera
2025-06-01, 15:20
  #45
Avstängd
Instruktionen ser ut att vara gjord för Windows, men fungerar Biblio-appen i windows?
Citera
2025-06-01, 15:25
  #46
Medlem
http://biblio.app
Jag har inte använt mig av appen. Kör via webbläsaren istället.
Jag gissar på att du kan logga in med samma användaruppgifter som i appen.

Citat:
Ursprungligen postat av maallgan
Instruktionen ser ut att vara gjord för Windows, men fungerar Biblio-appen i windows?
Citera
2025-06-01, 18:19
  #47
Medlem
Citat:
Ursprungligen postat av [BPS]
https://github.com/skunkmailen/BEPUB

"Mitmproxy" behövs inte längre.

Tampermonkey används för att få ut all nödvändig info från Biblio.
  • Länk till EPUB-filen.
  • User ID - ditt unika användar ID.
  • Key - nyckel för att låsa upp EPUB-filen.
Denna info kopieras därefter in i Python-skriptet som används för att skapa en .EPUB-fil utan DRM.

Viktigt att tänka på:
Infon du får från Tampermonkey är knuten till din användare, så posta inte infon här eller någon annanstans.

Snyggt, är detta enbart för Windows?
Citera
2025-06-01, 20:16
  #48
Medlem
Det bör även fungera på macOS och Unix, så länge du använder en webbläsare som stöder Tampermonkey samt har Python installerat.

Citat:
Ursprungligen postat av Buttergoeken
Snyggt, är detta enbart för Windows?
Citera

Skapa ett konto eller logga in för att kommentera

Du måste vara medlem för att kunna kommentera

Skapa ett konto

Det är enkelt att registrera ett nytt konto

Bli medlem

Logga in

Har du redan ett konto? Logga in här

Logga in