|
1 | 1 | /* |
2 | | - * Module: r2-streamer-kotlin |
3 | | - * Developers: Aferdita Muriqi, Clément Baumannn, Quentin Gliosca |
4 | | - * |
5 | | - * Copyright (c) 2020. Readium Foundation. All rights reserved. |
6 | | - * Use of this source code is governed by a BSD-style license which is detailed in the |
7 | | - * LICENSE file present in the project repository where this source code is maintained. |
| 2 | + * Copyright 2020 Readium Foundation. All rights reserved. |
| 3 | + * Use of this source code is governed by the BSD-style license |
| 4 | + * available in the top-level LICENSE file of the project. |
8 | 5 | */ |
9 | 6 |
|
10 | 7 | package org.readium.r2.streamer.parser.epub |
11 | 8 |
|
12 | 9 | import com.mcxiaoke.koi.HASH |
13 | 10 | import com.mcxiaoke.koi.ext.toHexBytes |
14 | | -import org.readium.r2.shared.fetcher.ProxyResource |
15 | | -import org.readium.r2.shared.fetcher.Resource |
16 | | -import org.readium.r2.shared.fetcher.ResourceTry |
17 | | -import org.readium.r2.shared.fetcher.mapCatching |
| 11 | +import org.readium.r2.shared.fetcher.* |
18 | 12 | import org.readium.r2.shared.publication.encryption.encryption |
19 | 13 | import kotlin.experimental.xor |
20 | 14 |
|
21 | 15 | internal class EpubDeobfuscator(private val pubId: String) { |
22 | 16 |
|
23 | 17 | fun transform(resource: Resource): Resource = DeobfuscatingResource(resource) |
24 | 18 |
|
25 | | - inner class DeobfuscatingResource(resource: Resource): ProxyResource(resource) { |
| 19 | + inner class DeobfuscatingResource(resource: Resource): TransformingResource(resource, cacheBytes = true) { |
26 | 20 |
|
27 | | - override suspend fun read(range: LongRange?): ResourceTry<ByteArray> { |
28 | | - val algorithm = resource.link().properties.encryption?.algorithm |
| 21 | + override suspend fun transform(data: ResourceTry<ByteArray>): ResourceTry<ByteArray> = |
| 22 | + data.map { bytes -> |
| 23 | + val algorithm = resource.link().properties.encryption?.algorithm |
29 | 24 |
|
30 | | - if (algorithm !in algorithm2length.keys) |
31 | | - return resource.read(range) |
| 25 | + val obfuscationLength: Int = algorithm2length[algorithm] |
| 26 | + ?: return@map bytes |
32 | 27 |
|
33 | | - return resource.read(range).mapCatching { |
34 | | - val obfuscationLength: Int = algorithm2length[algorithm]!! |
35 | 28 | val obfuscationKey: ByteArray = when (algorithm) { |
36 | 29 | "http://ns.adobe.com/pdf/enc#RC" -> getHashKeyAdobe(pubId) |
37 | 30 | else -> HASH.sha1(pubId).toHexBytes() |
38 | 31 | } |
39 | 32 |
|
40 | | - deobfuscate(it, range, obfuscationKey, obfuscationLength) |
41 | | - it |
| 33 | + deobfuscate(bytes = bytes, obfuscationKey = obfuscationKey, obfuscationLength = obfuscationLength) |
| 34 | + bytes |
42 | 35 | } |
43 | | - } |
44 | 36 | } |
45 | 37 |
|
46 | 38 | private val algorithm2length: Map<String, Int> = mapOf( |
47 | 39 | "http://www.idpf.org/2008/embedding" to 1040, |
48 | 40 | "http://ns.adobe.com/pdf/enc#RC" to 1024 |
49 | 41 | ) |
50 | 42 |
|
51 | | - private fun deobfuscate(bytes: ByteArray, range: LongRange?, obfuscationKey: ByteArray, obfuscationLength: Int) { |
| 43 | + private fun deobfuscate(bytes: ByteArray, obfuscationKey: ByteArray, obfuscationLength: Int) { |
52 | 44 | @Suppress("NAME_SHADOWING") |
53 | | - val range = range ?: (0L until bytes.size) |
54 | | - if (range.first >= obfuscationLength) { |
55 | | - return |
56 | | - } |
57 | | - |
58 | | - val toDeobfuscate = Math.max(range.first, 0L)..Math.min(range.last, obfuscationLength - 1L) |
59 | | - for (i in toDeobfuscate.map { it.toInt() }) |
| 45 | + val toDeobfuscate = 0 until obfuscationLength |
| 46 | + for (i in toDeobfuscate) |
60 | 47 | bytes[i] = bytes[i].xor(obfuscationKey[i % obfuscationKey.size]) |
61 | 48 | } |
62 | 49 |
|
|
0 commit comments