On LCP drm
What is LCP drm scheme
EPUB Lightweight Content Protection (LCP) is protection mechanism designed as an interoperable, vendor-neutral DRM solution. It functions by encrypting files within the EPUB archive (which is just a zip file) using AES-256 bit symmetric keys. In order for the user to read such protected ebooks, a password is provided to a supported reader.
LCP protected epubs are not delivered directly to the user. In Slovenia’s library system, when lending a book, only a license file is provided to me. License file has an lcpl extension and is json encoded. It contains:
- encryption profile
- encrypted value of content key
- link to epub file
- … (not important for this blog post)
The key (content key) to decrypt files within epub archive is calculated using aes256-cbc decryption where the ciphertext is the encrypted value of the content key. For this operation a key (I’ll call it user key) is used and this one is calculated using an algorithm from a class of functions called key derivation functions. The specific algorithms used are public (part of ISO/IEC 23078-2:2024) and calculate the user key from the password, only there is another secret key used as part of this key derivation process, making us mere mortals unable to calculate the user key even if we have the password.
LCP profiles
There are multiple algorithms used:
- basic - this one just uses sha256 hash of user password as the user key,
- profile-1.0 - this one uses an algorithm that iteratively uses sha256 combined with masterkey to derive the user key (this master key was already leaked on the internet),
- profile-2.[1-9] - these profiles use an algorithm unknown to me (I do not own a version of the ISO standard) and is the currently recommended version to use.
Diagram
To recap, the decryption diagram looks like this:
User Password -(key derivation)->
User Key -(aes256 decrypt)->
Content Key -(aes256 decrypt)->
Decrypted EPUB
Implementation details: Thorium reader
One of the supported epub readers on Linux is Thorium reader. This is an electron app distributed mainly as an asar archive. This archive also contains a resource file called lcp.node. I think we can deduce while looking at this file that it is a shared object file used by this app to do this protection removal. Looking also into this file we can even find references to aes 256:
# Example of checking for symbols in the native node module
LCP_NODE=/opt/Thorium/resources/app.asar.unpacked/external-assets/lcp.node
readelf -Ws $LCP_NODE | grep -i aes_256
Further analysis of the binary would cross the line into bypassing effective technological protection measures, which is outside the scope of this research. However, I think in the era of AIs any chatbot would reply with “just use gdb or bpftrace” with additional information if anyone gives this article as input. This makes removing drm a nice exercise for high-school level kids that want to read LCP books on their kindles.
Recommendations and conclusion
There are so many mistakes here making hackers’ jobs easier:
lcp.nodecould be named differently,- this same file could be extracted every time and loaded into node from some temporary location, not just be available statically on the disk,
- this file should be stripped, why are the location of aes256 functions marked?
Closing thought
When talking about DRM systems, security is achieved through obscurity and leaving so many steps unobscured makes security very weak.