Merge branch 'no-libsodium' into 'master'
No libsodium See merge request mangadex-pub/mangadex_at_home!65
This commit is contained in:
commit
602a43d62e
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -17,6 +17,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
## [1.2.2] - 2020-08-21
|
||||||
|
### Changed
|
||||||
|
- [2020-08-11] Moved to a Java implementation of NaCl [@carbotaniuman].
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- [2020-08-11] Revert WebUI changes in 1.2.1 [@carbotaniuman].
|
||||||
|
|
||||||
|
|
||||||
## [1.2.1] - 2020-08-11
|
## [1.2.1] - 2020-08-11
|
||||||
### Added
|
### Added
|
||||||
- [2020-08-11] New CLI for specifying database location, cache folder, and settings [@carbotaniuman].
|
- [2020-08-11] New CLI for specifying database location, cache folder, and settings [@carbotaniuman].
|
||||||
|
@ -234,7 +242,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Fixed
|
### Fixed
|
||||||
- [2020-06-11] Tweaked logging configuration to reduce log file sizes by [@carbotaniuman].
|
- [2020-06-11] Tweaked logging configuration to reduce log file sizes by [@carbotaniuman].
|
||||||
|
|
||||||
[Unreleased]: https://gitlab.com/mangadex/mangadex_at_home/-/compare/1.2.1...HEAD
|
[Unreleased]: https://gitlab.com/mangadex/mangadex_at_home/-/compare/1.2.2...HEAD
|
||||||
|
[1.2.1]: https://gitlab.com/mangadex/mangadex_at_home/-/compare/1.2.1...1.2.2
|
||||||
[1.2.1]: https://gitlab.com/mangadex/mangadex_at_home/-/compare/1.2.0...1.2.1
|
[1.2.1]: https://gitlab.com/mangadex/mangadex_at_home/-/compare/1.2.0...1.2.1
|
||||||
[1.2.0]: https://gitlab.com/mangadex/mangadex_at_home/-/compare/1.1.5...1.2.0
|
[1.2.0]: https://gitlab.com/mangadex/mangadex_at_home/-/compare/1.1.5...1.2.0
|
||||||
[1.1.5]: https://gitlab.com/mangadex/mangadex_at_home/-/compare/1.1.4...1.1.5
|
[1.1.5]: https://gitlab.com/mangadex/mangadex_at_home/-/compare/1.1.4...1.1.5
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
FROM openjdk:15-alpine
|
FROM openjdk:15-alpine
|
||||||
WORKDIR /mangahome
|
WORKDIR /mangahome
|
||||||
COPY /build/libs/mangadex_at_home.jar .
|
COPY /build/libs/mangadex_at_home.jar .
|
||||||
RUN apk update && apk add --no-cache libsodium
|
RUN apk update
|
||||||
VOLUME "/mangahome/cache"
|
VOLUME "/mangahome/cache"
|
||||||
EXPOSE 443 8080
|
EXPOSE 443 8080
|
||||||
CMD java -Dfile-level=off -Dstdout-level=trace -jar mangadex_at_home.jar
|
CMD java -Dfile-level=off -Dstdout-level=trace -jar mangadex_at_home.jar
|
|
@ -44,9 +44,6 @@ dependencies {
|
||||||
|
|
||||||
implementation group: "org.xerial", name: "sqlite-jdbc", version: "3.30.1"
|
implementation group: "org.xerial", name: "sqlite-jdbc", version: "3.30.1"
|
||||||
|
|
||||||
implementation "com.goterl.lazycode:lazysodium-java:4.3.0"
|
|
||||||
implementation "net.java.dev.jna:jna:5.5.0"
|
|
||||||
|
|
||||||
implementation "info.picocli:picocli:4.5.0"
|
implementation "info.picocli:picocli:4.5.0"
|
||||||
kapt "info.picocli:picocli-codegen:4.5.0"
|
kapt "info.picocli:picocli-codegen:4.5.0"
|
||||||
}
|
}
|
||||||
|
|
3425
src/main/java/mdnet/security/TweetNaclFast.java
Normal file
3425
src/main/java/mdnet/security/TweetNaclFast.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -21,7 +21,7 @@ package mdnet.base
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
const val CLIENT_BUILD = 18
|
const val CLIENT_BUILD = 19
|
||||||
|
|
||||||
@JvmField val MAX_AGE_CACHE: Duration = Duration.ofDays(14)
|
@JvmField val MAX_AGE_CACHE: Duration = Duration.ofDays(14)
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,6 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import com.goterl.lazycode.lazysodium.LazySodiumJava
|
|
||||||
import com.goterl.lazycode.lazysodium.SodiumJava
|
|
||||||
import com.goterl.lazycode.lazysodium.exceptions.SodiumException
|
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -54,6 +51,7 @@ import mdnet.base.trace
|
||||||
import mdnet.base.warn
|
import mdnet.base.warn
|
||||||
import mdnet.cache.CachingInputStream
|
import mdnet.cache.CachingInputStream
|
||||||
import mdnet.cache.DiskLruCache
|
import mdnet.cache.DiskLruCache
|
||||||
|
import mdnet.security.TweetNaclFast
|
||||||
import org.apache.http.client.config.CookieSpecs
|
import org.apache.http.client.config.CookieSpecs
|
||||||
import org.apache.http.client.config.RequestConfig
|
import org.apache.http.client.config.RequestConfig
|
||||||
import org.apache.http.impl.client.HttpClients
|
import org.apache.http.impl.client.HttpClients
|
||||||
|
@ -91,6 +89,7 @@ class ImageServer(
|
||||||
private val executor = Executors.newCachedThreadPool()
|
private val executor = Executors.newCachedThreadPool()
|
||||||
|
|
||||||
fun handler(dataSaver: Boolean, tokenized: Boolean = false): HttpHandler {
|
fun handler(dataSaver: Boolean, tokenized: Boolean = false): HttpHandler {
|
||||||
|
val box = TweetNaclFast.SecretBox(remoteSettings.tokenKey)
|
||||||
return baseHandler().then { request ->
|
return baseHandler().then { request ->
|
||||||
val chapterHash = Path.of("chapterHash")(request)
|
val chapterHash = Path.of("chapterHash")(request)
|
||||||
val fileName = Path.of("fileName")(request)
|
val fileName = Path.of("fileName")(request)
|
||||||
|
@ -114,19 +113,19 @@ class ImageServer(
|
||||||
}
|
}
|
||||||
val token = try {
|
val token = try {
|
||||||
JACKSON.readValue<Token>(
|
JACKSON.readValue<Token>(
|
||||||
try {
|
box.open(tokenArr.sliceArray(24 until tokenArr.size), tokenArr.sliceArray(0 until 24)).apply {
|
||||||
SODIUM.cryptoBoxOpenEasyAfterNm(
|
if (this == null) {
|
||||||
tokenArr.sliceArray(24 until tokenArr.size), tokenArr.sliceArray(0 until 24), remoteSettings.tokenKey
|
LOGGER.info { "Request for $sanitizedUri rejected for invalid token" }
|
||||||
)
|
return@then Response(Status.FORBIDDEN)
|
||||||
} catch (_: SodiumException) {
|
}
|
||||||
LOGGER.info { "Request for $sanitizedUri rejected for invalid token" }
|
|
||||||
return@then Response(Status.FORBIDDEN)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} catch (e: JsonProcessingException) {
|
} catch (e: JsonProcessingException) {
|
||||||
LOGGER.info { "Request for $sanitizedUri rejected for invalid token" }
|
println(e)
|
||||||
|
LOGGER.info(e) { "Request for $sanitizedUri rejected for invalid token" }
|
||||||
return@then Response(Status.FORBIDDEN)
|
return@then Response(Status.FORBIDDEN)
|
||||||
}
|
}
|
||||||
|
println(token)
|
||||||
|
|
||||||
if (OffsetDateTime.now().isAfter(token.expires)) {
|
if (OffsetDateTime.now().isAfter(token.expires)) {
|
||||||
LOGGER.info { "Request for $sanitizedUri rejected for expired token" }
|
LOGGER.info { "Request for $sanitizedUri rejected for expired token" }
|
||||||
|
@ -164,6 +163,7 @@ class ImageServer(
|
||||||
snapshot.close()
|
snapshot.close()
|
||||||
LOGGER.warn { "Removing broken cache file for $sanitizedUri" }
|
LOGGER.warn { "Removing broken cache file for $sanitizedUri" }
|
||||||
cache.removeUnsafe(imageId.toCacheId())
|
cache.removeUnsafe(imageId.toCacheId())
|
||||||
|
cache.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
request.handleCacheMiss(sanitizedUri, getRc4(rc4Bytes), imageId, imageDatum)
|
request.handleCacheMiss(sanitizedUri, getRc4(rc4Bytes), imageId, imageDatum)
|
||||||
|
@ -276,8 +276,11 @@ class ImageServer(
|
||||||
if (editor.getLength(0) == contentLength.toLong()) {
|
if (editor.getLength(0) == contentLength.toLong()) {
|
||||||
LOGGER.info { "Cache download for $sanitizedUri committed" }
|
LOGGER.info { "Cache download for $sanitizedUri committed" }
|
||||||
editor.commit()
|
editor.commit()
|
||||||
|
println("A")
|
||||||
|
cache.flush()
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn { "Cache download for $sanitizedUri aborted" }
|
LOGGER.warn { "Cache download for $sanitizedUri aborted" }
|
||||||
|
println("B")
|
||||||
editor.abort()
|
editor.abort()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -317,7 +320,6 @@ class ImageServer(
|
||||||
.header("X-Cache", if (cached) "HIT" else "MISS")
|
.header("X-Cache", if (cached) "HIT" else "MISS")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val SODIUM = LazySodiumJava(SodiumJava())
|
|
||||||
private val JACKSON: ObjectMapper = jacksonObjectMapper()
|
private val JACKSON: ObjectMapper = jacksonObjectMapper()
|
||||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||||
.registerModule(JavaTimeModule())
|
.registerModule(JavaTimeModule())
|
||||||
|
|
|
@ -19,31 +19,10 @@ along with this MangaDex@Home. If not, see <http://www.gnu.org/licenses/>.
|
||||||
/* ktlint-disable no-wildcard-imports */
|
/* ktlint-disable no-wildcard-imports */
|
||||||
package mdnet.base.server
|
package mdnet.base.server
|
||||||
|
|
||||||
import com.goterl.lazycode.lazysodium.LazySodiumJava
|
|
||||||
import com.goterl.lazycode.lazysodium.exceptions.SodiumException
|
|
||||||
import com.goterl.lazycode.lazysodium.interfaces.Box
|
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
@Throws(SodiumException::class)
|
|
||||||
fun LazySodiumJava.cryptoBoxOpenEasyAfterNm(cipherBytes: ByteArray, nonce: ByteArray, sharedKey: ByteArray): String {
|
|
||||||
if (!Box.Checker.checkNonce(nonce.size)) {
|
|
||||||
throw SodiumException("Incorrect nonce length.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Box.Checker.checkBeforeNmBytes(sharedKey.size)) {
|
|
||||||
throw SodiumException("Incorrect shared secret key length.")
|
|
||||||
}
|
|
||||||
|
|
||||||
val message = ByteArray(cipherBytes.size - Box.MACBYTES)
|
|
||||||
val res: Boolean = cryptoBoxOpenEasyAfterNm(message, cipherBytes, cipherBytes.size.toLong(), nonce, sharedKey)
|
|
||||||
if (!res) {
|
|
||||||
throw SodiumException("Could not fully complete shared secret key decryption.")
|
|
||||||
}
|
|
||||||
return str(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getRc4(key: ByteArray): Cipher {
|
fun getRc4(key: ByteArray): Cipher {
|
||||||
val rc4 = Cipher.getInstance("RC4")
|
val rc4 = Cipher.getInstance("RC4")
|
||||||
rc4.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "RC4"))
|
rc4.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "RC4"))
|
Loading…
Reference in a new issue