package org.schabi.newpipe.util import android.content.pm.PackageManager import android.content.pm.Signature import androidx.core.content.pm.PackageInfoCompat import org.schabi.newpipe.App import org.schabi.newpipe.error.ErrorInfo import org.schabi.newpipe.error.ErrorUtil.Companion.createNotification import org.schabi.newpipe.error.UserAction import java.io.ByteArrayInputStream import java.io.InputStream import java.security.MessageDigest import java.security.NoSuchAlgorithmException import java.security.cert.CertificateEncodingException import java.security.cert.CertificateException import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.time.Instant import java.time.ZonedDateTime import java.time.format.DateTimeFormatter object ReleaseVersionUtil { // Public key of the certificate that is used in NewPipe release versions private const val RELEASE_CERT_PUBLIC_KEY_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15" @JvmStatic fun isReleaseApk(): Boolean { return certificateSHA1Fingerprint == RELEASE_CERT_PUBLIC_KEY_SHA1 } /** * Method to get the APK's SHA1 key. See https://stackoverflow.com/questions/9293019/#22506133. * * @return String with the APK's SHA1 fingerprint in hexadecimal */ private val certificateSHA1Fingerprint: String get() { val app = App.getApp() val signatures: List = try { PackageInfoCompat.getSignatures(app.packageManager, app.packageName) } catch (e: PackageManager.NameNotFoundException) { showRequestError(app, e, "Could not find package info") return "" } if (signatures.isEmpty()) { return "" } val x509cert = try { val cert = signatures[0].toByteArray() val input: InputStream = ByteArrayInputStream(cert) val cf = CertificateFactory.getInstance("X509") cf.generateCertificate(input) as X509Certificate } catch (e: CertificateException) { showRequestError(app, e, "Certificate error") return "" } return try { val md = MessageDigest.getInstance("SHA1") val publicKey = md.digest(x509cert.encoded) byte2HexFormatted(publicKey) } catch (e: NoSuchAlgorithmException) { showRequestError(app, e, "Could not retrieve SHA1 key") "" } catch (e: CertificateEncodingException) { showRequestError(app, e, "Could not retrieve SHA1 key") "" } } private fun byte2HexFormatted(arr: ByteArray): String { val str = StringBuilder(arr.size * 2) for (i in arr.indices) { var h = Integer.toHexString(arr[i].toInt()) val l = h.length if (l == 1) { h = "0$h" } if (l > 2) { h = h.substring(l - 2, l) } str.append(h.uppercase()) if (i < arr.size - 1) { str.append(':') } } return str.toString() } private fun showRequestError(app: App, e: Exception, request: String) { createNotification( app, ErrorInfo(e, UserAction.CHECK_FOR_NEW_APP_VERSION, request) ) } fun isLastUpdateCheckExpired(expiry: Long): Boolean { return Instant.ofEpochSecond(expiry).isBefore(Instant.now()) } /** * Coerce expiry date time in between 6 hours and 72 hours from now * * @return Epoch second of expiry date time */ fun coerceUpdateCheckExpiry(expiryString: String?): Long { val now = ZonedDateTime.now() return expiryString?.let { var expiry = ZonedDateTime.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(expiryString)) expiry = maxOf(expiry, now.plusHours(6)) expiry = minOf(expiry, now.plusHours(72)) expiry.toEpochSecond() } ?: now.plusHours(6).toEpochSecond() } }