Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ All notable changes to this project will be documented in this file. Take a look

* Support for asynchronous callbacks with `onCreatePublication` (contributed by [@smoores-dev](https://github.com/readium/swift-toolkit/pull/673)).

### Fixed

#### LCP

* Fixed crash when an EPUB resource is declared as LCP-encrypted in the manifest but contains unencrypted data.


## [3.5.0]

Expand Down
19 changes: 17 additions & 2 deletions Sources/LCP/Content Protection/LCPDecryptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import ReadiumInternal
import ReadiumShared

private let lcpScheme = "http://readium.org/2014/01/lcp"
private let AESBlockSize: UInt64 = 16 // bytes

/// Decrypts a resource protected with LCP.
final class LCPDecryptor {
Expand Down Expand Up @@ -117,7 +116,7 @@ final class LCPDecryptor {
guard let length = length else {
return failure(.requiredEstimatedLength)
}
guard length >= 2 * AESBlockSize else {
guard length.isValidAESChunk else {
return failure(.invalidCBCData)
}

Expand Down Expand Up @@ -207,6 +206,10 @@ final class LCPDecryptor {
private extension LCPLicense {
func decryptFully(data: ReadResult<Data>, isDeflated: Bool) async -> ReadResult<Data> {
data.flatMap {
guard UInt64($0.count).isValidAESChunk else {
return .failure(.decoding(LCPDecryptor.Error.invalidCBCData))
}

do {
// Decrypts the resource.
guard var data = try self.decipher($0) else {
Expand Down Expand Up @@ -242,3 +245,15 @@ private extension ReadiumShared.Encryption {
algorithm == "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
}
}

private let AESBlockSize: UInt64 = 16 // bytes

private extension UInt64 {
/// Checks if this number is a valid CBC length - i.e. a multiple of AES
/// block size and at least 2 blocks (IV + data).
/// If not, the file is likely not actually encrypted despite being declared
/// as such.
var isValidAESChunk: Bool {
self >= 2 * AESBlockSize && self % AESBlockSize == 0
}
}