additional boot signature support and others

Issue #90: support magisk-modified uncompressed ramdisk
pull/94/head
cfig 3 years ago
parent 0a342363de
commit 067cab9406
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

1
.gitignore vendored

@ -2,3 +2,4 @@
.gradle .gradle
build/ build/
local.properties local.properties
__pycache__

@ -23,7 +23,7 @@ import avb.desc.UnknownDescriptor
import cfig.Avb import cfig.Avb
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.Companion.paddingWith import cfig.helper.Helper.Companion.paddingWith
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.codec.binary.Hex import org.apache.commons.codec.binary.Hex
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -93,24 +93,24 @@ class AVBInfo(
var vbMetaOffset: Long var vbMetaOffset: Long
) )
private fun imageGlance(dataSrc: DataSrc<*>): Glance { private fun imageGlance(dp: Dumpling<*>): Glance {
val ret = Glance(null, 0) val ret = Glance(null, 0)
try { try {
ret.footer = Footer(dataSrc.readFully(Pair(-Footer.SIZE.toLong(), Footer.SIZE))) ret.footer = Footer(dp.readFully(Pair(-Footer.SIZE.toLong(), Footer.SIZE)))
ret.vbMetaOffset = ret.footer!!.vbMetaOffset ret.vbMetaOffset = ret.footer!!.vbMetaOffset
log.info("${dataSrc.getName()}: $ret.footer") log.info("${dp.getName()}: $ret.footer")
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
log.info("image ${dataSrc.getName()} has no AVB Footer") log.info("image ${dp.getName()} has no AVB Footer")
} }
return ret return ret
} }
fun parseFrom(dataSrc: DataSrc<*>): AVBInfo { fun parseFrom(dp: Dumpling<*>): AVBInfo {
log.info("parseFrom(${dataSrc.getName()}) ...") log.info("parseFrom(${dp.getName()}) ...")
// glance // glance
val (footer, vbMetaOffset) = imageGlance(dataSrc) val (footer, vbMetaOffset) = imageGlance(dp)
// header // header
val vbMetaHeader = Header(dataSrc.readFully(Pair(vbMetaOffset, Header.SIZE))) val vbMetaHeader = Header(dp.readFully(Pair(vbMetaOffset, Header.SIZE)))
log.debug(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(vbMetaHeader)) log.debug(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(vbMetaHeader))
val atlas = mutableMapOf<String, Pair<Long, Int>>() val atlas = mutableMapOf<String, Pair<Long, Int>>()
@ -126,9 +126,9 @@ class AVBInfo(
val ai = AVBInfo(vbMetaHeader, null, AuxBlob(), footer) val ai = AVBInfo(vbMetaHeader, null, AuxBlob(), footer)
// Auth blob // Auth blob
if (vbMetaHeader.authentication_data_block_size > 0) { if (vbMetaHeader.authentication_data_block_size > 0) {
val ba = dataSrc.readFully(atlas["auth.hash"]!!) val ba = dp.readFully(atlas["auth.hash"]!!)
log.debug("Parsed Auth Hash (Header & Aux Blob): " + Hex.encodeHexString(ba)) log.debug("Parsed Auth Hash (Header & Aux Blob): " + Hex.encodeHexString(ba))
val bb = dataSrc.readFully(atlas["auth.sig"]!!) val bb = dp.readFully(atlas["auth.sig"]!!)
log.debug("Parsed Auth Signature (of hash): " + Hex.encodeHexString(bb)) log.debug("Parsed Auth Signature (of hash): " + Hex.encodeHexString(bb))
ai.authBlob = AuthBlob() ai.authBlob = AuthBlob()
ai.authBlob!!.offset = atlas["auth"]!!.first ai.authBlob!!.offset = atlas["auth"]!!.first
@ -137,7 +137,7 @@ class AVBInfo(
ai.authBlob!!.signature = Hex.encodeHexString(bb) ai.authBlob!!.signature = Hex.encodeHexString(bb)
} }
// aux // aux
val rawAuxBlob = dataSrc.readFully(atlas["aux"]!!) val rawAuxBlob = dp.readFully(atlas["aux"]!!)
// aux - desc // aux - desc
if (vbMetaHeader.descriptors_size > 0) { if (vbMetaHeader.descriptors_size > 0) {
val descriptors = UnknownDescriptor.parseDescriptors( val descriptors = UnknownDescriptor.parseDescriptors(
@ -172,7 +172,7 @@ class AVBInfo(
) )
log.debug("Parsed Pub Key Metadata: " + Helper.toHexString(ai.auxBlob!!.pubkeyMeta!!.pkmd)) log.debug("Parsed Pub Key Metadata: " + Helper.toHexString(ai.auxBlob!!.pubkeyMeta!!.pkmd))
} }
log.debug("vbmeta info of [${dataSrc.getName()}] has been analyzed") log.debug("vbmeta info of [${dp.getName()}] has been analyzed")
return ai return ai
} }
} }

@ -24,7 +24,8 @@ import avb.desc.HashDescriptor
import cfig.helper.CryptoHelper import cfig.helper.CryptoHelper
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.Companion.paddingWith import cfig.helper.Helper.Companion.paddingWith
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.codec.binary.Hex import org.apache.commons.codec.binary.Hex
import org.apache.commons.exec.CommandLine import org.apache.commons.exec.CommandLine
@ -205,7 +206,7 @@ class Avb {
it.auxBlob!!.hashDescriptors.get(0).partition_name it.auxBlob!!.hashDescriptors.get(0).partition_name
} }
//read hashDescriptor from image //read hashDescriptor from image
val newHashDesc = AVBInfo.parseFrom(DataSrc("$fileName.signed")) val newHashDesc = AVBInfo.parseFrom(Dumpling("$fileName.signed"))
check(newHashDesc.auxBlob!!.hashDescriptors.size == 1) check(newHashDesc.auxBlob!!.hashDescriptors.size == 1)
var seq = -1 //means not found var seq = -1 //means not found
//main vbmeta //main vbmeta
@ -236,32 +237,22 @@ class Avb {
} }
} }
fun verify(ai: AVBInfo, image_file: String, parent: String = ""): Array<Any> { fun verify(ai: AVBInfo, dp: Dumpling<*>, parent: String = ""): Array<Any> {
val ret: Array<Any> = arrayOf(true, "") val ret: Array<Any> = arrayOf(true, "")
val localParent = parent.ifEmpty { image_file } val localParent = parent.ifEmpty { dp.getLabel() }
//header //header
val rawHeaderBlob = DataSrc(image_file).readFully(Pair(ai.footer?.vbMetaOffset ?: 0, Header.SIZE)) val rawHeaderBlob = dp.readFully(Pair(ai.footer?.vbMetaOffset ?: 0, Header.SIZE))
// aux // aux
val vbOffset = ai.footer?.vbMetaOffset ?: 0 val vbOffset = ai.footer?.vbMetaOffset ?: 0
//@formatter:off //@formatter:off
val rawAuxBlob = DataSrc(image_file).readFully( val rawAuxBlob = dp.readFully(
Pair(vbOffset + Header.SIZE + ai.header!!.authentication_data_block_size, Pair(vbOffset + Header.SIZE + ai.header!!.authentication_data_block_size,
ai.header!!.auxiliary_data_block_size.toInt())) ai.header!!.auxiliary_data_block_size.toInt()))
//@formatter:on //@formatter:on
//integrity check //integrity check
val declaredAlg = Algorithms.get(ai.header!!.algorithm_type) val declaredAlg = Algorithms.get(ai.header!!.algorithm_type)
if (declaredAlg!!.public_key_num_bytes > 0) { if (declaredAlg!!.public_key_num_bytes > 0) {
val gkiPubKey = if (declaredAlg.algorithm_type == 1) AuxBlob.encodePubKey( inspectKey(ai)
declaredAlg,
File("aosp/make/target/product/gsi/testkey_rsa2048.pem").readBytes()
) else null
if (AuxBlob.encodePubKey(declaredAlg).contentEquals(ai.auxBlob!!.pubkey!!.pubkey)) {
log.info("VERIFY($localParent): signed with dev key: " + declaredAlg.defaultKey)
} else if (gkiPubKey.contentEquals(ai.auxBlob!!.pubkey!!.pubkey)) {
log.info("VERIFY($localParent): signed with dev GKI key: " + declaredAlg.defaultKey)
} else {
log.info("VERIFY($localParent): signed with release key")
}
val calcHash = val calcHash =
Helper.join(declaredAlg.padding, AuthBlob.calcHash(rawHeaderBlob, rawAuxBlob, declaredAlg.name)) Helper.join(declaredAlg.padding, AuthBlob.calcHash(rawHeaderBlob, rawAuxBlob, declaredAlg.name))
val readHash = Helper.join(declaredAlg.padding, Helper.fromHexString(ai.authBlob!!.hash!!)) val readHash = Helper.join(declaredAlg.padding, Helper.fromHexString(ai.authBlob!!.hash!!))
@ -293,7 +284,7 @@ class Avb {
ai.auxBlob!!.chainPartitionDescriptors.forEach { ai.auxBlob!!.chainPartitionDescriptors.forEach {
val vRet = it.verify( val vRet = it.verify(
prefixes.map { prefix -> "$prefix${it.partition_name}.img" }, prefixes.map { prefix -> "$prefix${it.partition_name}.img" },
image_file + "->Chain[${it.partition_name}]" dp.getLabel() + "->Chain[${it.partition_name}]"
) )
if (vRet[0] as Boolean) { if (vRet[0] as Boolean) {
log.info("VERIFY($localParent->Chain[${it.partition_name}]): " + "PASS") log.info("VERIFY($localParent->Chain[${it.partition_name}]): " + "PASS")
@ -307,7 +298,7 @@ class Avb {
ai.auxBlob!!.hashDescriptors.forEach { ai.auxBlob!!.hashDescriptors.forEach {
val vRet = it.verify( val vRet = it.verify(
prefixes.map { prefix -> "$prefix${it.partition_name}.img" }, prefixes.map { prefix -> "$prefix${it.partition_name}.img" },
image_file + "->HashDescriptor[${it.partition_name}]" dp.getLabel() + "->HashDescriptor[${it.partition_name}]"
) )
if (vRet[0] as Boolean) { if (vRet[0] as Boolean) {
log.info("VERIFY($localParent->HashDescriptor[${it.partition_name}]): ${it.hash_algorithm} " + "PASS") log.info("VERIFY($localParent->HashDescriptor[${it.partition_name}]): ${it.hash_algorithm} " + "PASS")
@ -321,7 +312,7 @@ class Avb {
ai.auxBlob!!.hashTreeDescriptors.forEach { ai.auxBlob!!.hashTreeDescriptors.forEach {
val vRet = it.verify( val vRet = it.verify(
prefixes.map { prefix -> "$prefix${it.partition_name}.img" }, prefixes.map { prefix -> "$prefix${it.partition_name}.img" },
image_file + "->HashTreeDescriptor[${it.partition_name}]" dp.getLabel() + "->HashTreeDescriptor[${it.partition_name}]"
) )
if (vRet[0] as Boolean) { if (vRet[0] as Boolean) {
log.info("VERIFY($localParent->HashTreeDescriptor[${it.partition_name}]): ${it.hash_algorithm} " + "PASS") log.info("VERIFY($localParent->HashTreeDescriptor[${it.partition_name}]): ${it.hash_algorithm} " + "PASS")
@ -334,5 +325,51 @@ class Avb {
return ret return ret
} }
fun inspectKey(ai: AVBInfo): String {
var ret = "NONE"
if (ai.auxBlob!!.pubkey == null) {
log.info("vbmeta blob is unsigned")
return ret
}
val declaredAlg = Algorithms.get(ai.header!!.algorithm_type)
val gkiPubKey = if (declaredAlg!!.algorithm_type == 1) AuxBlob.encodePubKey(
declaredAlg,
File("aosp/make/target/product/gsi/testkey_rsa2048.pem").readBytes()
) else null
if (AuxBlob.encodePubKey(declaredAlg).contentEquals(ai.auxBlob!!.pubkey!!.pubkey)) {
log.info("signed with dev key: " + declaredAlg.defaultKey)
ret = declaredAlg.defaultKey
} else if (gkiPubKey.contentEquals(ai.auxBlob!!.pubkey!!.pubkey)) {
log.info("signed with dev GKI key: " + declaredAlg.defaultKey)
ret = declaredAlg.defaultKey
} else {
val keys = ObjectMapper().readValue(
this::class.java.classLoader.getResource("known_keys.json"),
object : TypeReference<List<KnownPublicKey>>() {})
val found = keys.filter { it.pubk.contentEquals(ai.auxBlob!!.pubkey!!.pubkey) }
if (found.isNotEmpty()) {
log.info("signed with release key: '${found[0].device}' by ${found[0].manufacturer}")
log.warn("Found key: ${found[0].toShortString()}")
ret = "${found[0].device} by ${found[0].manufacturer}"
} else {
log.info("signed with release key")
ret = "private release key"
}
}
return ret
}
data class KnownPublicKey(
var device: String = "",
var manufacturer: String = "",
var algorithm: String = "",
var pubk: ByteArray = byteArrayOf(),
var sha1: String = ""
) {
fun toShortString(): String {
return "PublicKey(device='$device' by '$manufacturer', algorithm='$algorithm', sha1='$sha1')"
}
}
} }
} }

@ -49,7 +49,7 @@ class Algorithms {
intArrayOf(0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, intArrayOf(0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20)), 0x00, 0x04, 0x20)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa2048.pem") defaultKey = "aosp/avb/data/testkey_rsa2048.pem")
val SHA256_RSA4096 = Algorithm( val SHA256_RSA4096 = Algorithm(
name = "SHA256_RSA4096", name = "SHA256_RSA4096",
@ -66,7 +66,7 @@ class Algorithms {
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20) 0x00, 0x04, 0x20)
), ),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa4096.pem" defaultKey = "aosp/avb/data/testkey_rsa4096.pem"
) )
val SHA256_RSA8192 = Algorithm( val SHA256_RSA8192 = Algorithm(
@ -83,7 +83,7 @@ class Algorithms {
intArrayOf(0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, intArrayOf(0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20)), 0x00, 0x04, 0x20)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa8192.pem") defaultKey = "aosp/avb/data/testkey_rsa8192.pem")
val SHA512_RSA2048 = Algorithm( val SHA512_RSA2048 = Algorithm(
name = "SHA512_RSA2048", name = "SHA512_RSA2048",
@ -99,7 +99,7 @@ class Algorithms {
intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40)), 0x00, 0x04, 0x40)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa2048.pem") defaultKey = "aosp/avb/data/testkey_rsa2048.pem")
val SHA512_RSA4096 = Algorithm( val SHA512_RSA4096 = Algorithm(
name = "SHA512_RSA4096", name = "SHA512_RSA4096",
@ -115,7 +115,7 @@ class Algorithms {
intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40)), 0x00, 0x04, 0x40)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa4096.pem") defaultKey = "aosp/avb/data/testkey_rsa4096.pem")
val SHA512_RSA8192 = Algorithm( val SHA512_RSA8192 = Algorithm(
name = "SHA512_RSA8192", name = "SHA512_RSA8192",
@ -132,7 +132,7 @@ class Algorithms {
intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40)), 0x00, 0x04, 0x40)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa8192.pem") defaultKey = "aosp/avb/data/testkey_rsa8192.pem")
algMap[NONE.name] = NONE algMap[NONE.name] = NONE

@ -17,7 +17,7 @@ package avb.desc
import avb.AVBInfo import avb.AVBInfo
import cfig.Avb import cfig.Avb
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import cc.cfig.io.Struct import cc.cfig.io.Struct
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@ -86,10 +86,10 @@ class ChainPartitionDescriptor(
val ret: Array<Any> = arrayOf(false, "file not found") val ret: Array<Any> = arrayOf(false, "file not found")
for (item in image_files) { for (item in image_files) {
if (File(item).exists()) { if (File(item).exists()) {
val subAi = AVBInfo.parseFrom(DataSrc(item)) val subAi = AVBInfo.parseFrom(Dumpling(item))
if (pubkey.contentEquals(subAi.auxBlob!!.pubkey!!.pubkey)) { if (pubkey.contentEquals(subAi.auxBlob!!.pubkey!!.pubkey)) {
log.info("VERIFY($parent): public key matches, PASS") log.info("VERIFY($parent): public key matches, PASS")
return Avb.verify(subAi, item, parent) return Avb.verify(subAi, Dumpling(item), parent)
} else { } else {
log.info("VERIFY($parent): public key mismatch, FAIL") log.info("VERIFY($parent): public key mismatch, FAIL")
ret[1] = "public key mismatch" ret[1] = "public key mismatch"

@ -17,7 +17,7 @@ package avb.desc
import avb.blob.Header import avb.blob.Header
import cfig.helper.CryptoHelper import cfig.helper.CryptoHelper
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import cc.cfig.io.Struct import cc.cfig.io.Struct
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.* import java.io.*
@ -113,7 +113,7 @@ class HashTreeDescriptor(
for (item in fileNames) { for (item in fileNames) {
if (File(item).exists()) { if (File(item).exists()) {
val trimmedHash = this.genMerkleTree(item, "hash.tree") val trimmedHash = this.genMerkleTree(item, "hash.tree")
val readTree = DataSrc(item).readFully(Pair(this.tree_offset, this.tree_size.toInt())) val readTree = Dumpling(item).readFully(Pair(this.tree_offset, this.tree_size.toInt()))
val ourHtHash = CryptoHelper.Hasher.sha256(File("hash.tree").readBytes()) val ourHtHash = CryptoHelper.Hasher.sha256(File("hash.tree").readBytes())
val diskHtHash = CryptoHelper.Hasher.sha256(readTree) val diskHtHash = CryptoHelper.Hasher.sha256(readTree)
if (!ourHtHash.contentEquals(diskHtHash)) { if (!ourHtHash.contentEquals(diskHtHash)) {

@ -160,6 +160,14 @@ class Common {
ZipHelper.lz4cat(s.dumpFile + ".lz4", s.dumpFile) ZipHelper.lz4cat(s.dumpFile + ".lz4", s.dumpFile)
ret = "lz4" ret = "lz4"
} }
ZipHelper.isAndroidCpio(s.dumpFile) -> {
log.info("ramdisk is uncompressed cpio")
Files.copy(
Paths.get(s.dumpFile), Paths.get(s.dumpFile + ".cpio"),
java.nio.file.StandardCopyOption.REPLACE_EXISTING
)
ret = "cpio"
}
else -> { else -> {
throw IllegalArgumentException("ramdisk is in unknown format") throw IllegalArgumentException("ramdisk is in unknown format")
} }
@ -273,6 +281,11 @@ class Common {
AndroidCpio().pack(root, f, "${f}_filelist.txt") AndroidCpio().pack(root, f, "${f}_filelist.txt")
FileInputStream(f).use { ZipHelper.xz(ramdiskGz, it) } FileInputStream(f).use { ZipHelper.xz(ramdiskGz, it) }
} }
ramdiskGz.endsWith(".cpio") -> {
val f = ramdiskGz.removeSuffix(".cpio")
AndroidCpio().pack(root, f, "${f}_filelist.txt")
File(f).copyTo(File(ramdiskGz), true)
}
else -> { else -> {
throw IllegalArgumentException("$ramdiskGz is not supported") throw IllegalArgumentException("$ramdiskGz is not supported")
} }

@ -19,9 +19,10 @@ import cfig.Avb
import cfig.bootimg.Common import cfig.bootimg.Common
import cfig.bootimg.Common.Companion.deleleIfExists import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Signer import cfig.bootimg.Signer
import cfig.bootimg.v3.BootV3
import cfig.bootimg.v3.VendorBoot import cfig.bootimg.v3.VendorBoot
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import cfig.packable.VBMetaParser import cfig.packable.VBMetaParser
import cfig.utils.EnvironmentVerifier import cfig.utils.EnvironmentVerifier
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
@ -77,6 +78,7 @@ data class BootV2(
companion object { companion object {
private val log = LoggerFactory.getLogger(BootV2::class.java) private val log = LoggerFactory.getLogger(BootV2::class.java)
private val workDir = Helper.prop("workDir") private val workDir = Helper.prop("workDir")
private val mapper = ObjectMapper()
fun parse(fileName: String): BootV2 { fun parse(fileName: String): BootV2 {
val ret = BootV2() val ret = BootV2()
@ -184,9 +186,8 @@ data class BootV2(
} }
fun extractImages(): BootV2 { fun extractImages(): BootV2 {
val workDir = Helper.prop("workDir")
//info //info
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this) mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this)
//kernel //kernel
Common.dumpKernel(Helper.Slice(info.output, kernel.position.toInt(), kernel.size, kernel.file!!)) Common.dumpKernel(Helper.Slice(info.output, kernel.position.toInt(), kernel.size, kernel.file!!))
//ramdisk //ramdisk
@ -196,7 +197,7 @@ data class BootV2(
) )
this.ramdisk.file = this.ramdisk.file!! + ".$fmt" this.ramdisk.file = this.ramdisk.file!! + ".$fmt"
//dump info again //dump info again
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
} }
//second bootloader //second bootloader
secondBootloader?.let { secondBootloader?.let {
@ -226,7 +227,7 @@ data class BootV2(
fun extractVBMeta(): BootV2 { fun extractVBMeta(): BootV2 {
if (this.info.verify.startsWith("VB2.0")) { if (this.info.verify.startsWith("VB2.0")) {
AVBInfo.parseFrom(DataSrc(info.output)).dumpDefault(info.output) AVBInfo.parseFrom(Dumpling(info.output)).dumpDefault(info.output)
if (File("vbmeta.img").exists()) { if (File("vbmeta.img").exists()) {
log.warn("Found vbmeta.img, parsing ...") log.warn("Found vbmeta.img, parsing ...")
VBMetaParser().unpack("vbmeta.img") VBMetaParser().unpack("vbmeta.img")
@ -238,7 +239,6 @@ data class BootV2(
} }
fun printSummary(): BootV2 { fun printSummary(): BootV2 {
val workDir = Helper.prop("workDir")
val tableHeader = AsciiTable().apply { val tableHeader = AsciiTable().apply {
addRule() addRule()
addRow("What", "Where") addRow("What", "Where")
@ -254,7 +254,17 @@ data class BootV2(
} else { } else {
"verify fail" "verify fail"
} }
it.addRow("AVB info [$verifyStatus]", Avb.getJsonFileName(info.output)) Avb.getJsonFileName(info.output).let { jsonFile ->
it.addRow("AVB info [$verifyStatus]", jsonFile)
if (File(jsonFile).exists()) {
mapper.readValue(File(jsonFile), AVBInfo::class.java).let { ai ->
val inspectRet = Avb.inspectKey(ai)
if (inspectRet != "NONE") {
it.addRow("\\-- signing key", inspectRet)
}
}
}
}
} }
//kernel //kernel
it.addRule() it.addRule()

@ -21,7 +21,7 @@ import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Signer import cfig.bootimg.Signer
import cfig.bootimg.v3.VendorBoot import cfig.bootimg.v3.VendorBoot
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import cfig.packable.VBMetaParser import cfig.packable.VBMetaParser
import cfig.utils.EnvironmentVerifier import cfig.utils.EnvironmentVerifier
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
@ -245,7 +245,7 @@ data class BootV2Dialects(
fun extractVBMeta(): BootV2Dialects { fun extractVBMeta(): BootV2Dialects {
if (this.info.verify.startsWith("VB2.0")) { if (this.info.verify.startsWith("VB2.0")) {
AVBInfo.parseFrom(DataSrc(info.output)).dumpDefault(info.output) AVBInfo.parseFrom(Dumpling(info.output)).dumpDefault(info.output)
if (File("vbmeta.img").exists()) { if (File("vbmeta.img").exists()) {
log.warn("Found vbmeta.img, parsing ...") log.warn("Found vbmeta.img, parsing ...")
VBMetaParser().unpack("vbmeta.img") VBMetaParser().unpack("vbmeta.img")

@ -23,7 +23,7 @@ import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Common.Companion.getPaddingSize import cfig.bootimg.Common.Companion.getPaddingSize
import cfig.bootimg.Signer import cfig.bootimg.Signer
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import cfig.packable.VBMetaParser import cfig.packable.VBMetaParser
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import de.vandermeer.asciitable.AsciiTable import de.vandermeer.asciitable.AsciiTable
@ -241,7 +241,7 @@ data class BootV3(
fun extractVBMeta(): BootV3 { fun extractVBMeta(): BootV3 {
// vbmeta in image // vbmeta in image
try { try {
AVBInfo.parseFrom(DataSrc(info.output)).dumpDefault(info.output) val ai = AVBInfo.parseFrom(Dumpling(info.output)).dumpDefault(info.output)
if (File("vbmeta.img").exists()) { if (File("vbmeta.img").exists()) {
log.warn("Found vbmeta.img, parsing ...") log.warn("Found vbmeta.img, parsing ...")
VBMetaParser().unpack("vbmeta.img") VBMetaParser().unpack("vbmeta.img")
@ -253,15 +253,20 @@ data class BootV3(
//GKI 1.0 bootsig //GKI 1.0 bootsig
if (info.signatureSize > 0) { if (info.signatureSize > 0) {
Helper.extractFile( log.info("GKI 1.0 signature")
info.output, this.bootSignature.file, Dumpling(info.output).readFully(Pair(this.bootSignature.position.toLong(), this.bootSignature.size))
this.bootSignature.position.toLong(), this.bootSignature.size .let { bootsigData ->
) File(this.bootSignature.file).writeBytes(bootsigData)
if (bootsigData.any { it.toInt() != 0 }) {
try { try {
val bootsig = AVBInfo.parseFrom(DataSrc(this.bootSignature.file)).dumpDefault(this.bootSignature.file) val bootsig = AVBInfo.parseFrom(Dumpling(bootsigData)).dumpDefault(this.bootSignature.file)
Avb.verify(bootsig, "${workDir}bootsig") Avb.verify(bootsig, Dumpling(bootsigData, "bootsig"))
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
log.warn("boot signature is invalid") log.warn("GKI 1.0 boot signature is invalid")
}
} else {
log.warn("GKI 1.0 boot signature has only NULL data")
}
} }
return this return this
} }
@ -277,18 +282,22 @@ data class BootV3(
AVBInfo::class.java AVBInfo::class.java
) )
val bootSig16kData = val bootSig16kData =
DataSrc(DataSrc(info.output).readFully(Pair(mainBlob.footer!!.originalImageSize - 16 * 1024, 16 * 1024))) Dumpling(Dumpling(info.output).readFully(Pair(mainBlob.footer!!.originalImageSize - 16 * 1024, 16 * 1024)))
try { try {
val blob1 = AVBInfo.parseFrom(bootSig16kData) val blob1 = AVBInfo.parseFrom(bootSig16kData)
.also { it.dumpDefault("bootsig." + it.auxBlob!!.hashDescriptors[0].partition_name) } .also { check(it.auxBlob!!.hashDescriptors[0].partition_name == "boot") }
.also { it.dumpDefault("sig.boot") }
val blob2 = val blob2 =
AVBInfo.parseFrom(DataSrc(bootSig16kData.readFully(blob1.encode().size until bootSig16kData.getLength()))) AVBInfo.parseFrom(Dumpling(bootSig16kData.readFully(blob1.encode().size until bootSig16kData.getLength())))
.also { it.dumpDefault("bootsig." + it.auxBlob!!.hashDescriptors[0].partition_name) } .also { check(it.auxBlob!!.hashDescriptors[0].partition_name == "generic_kernel") }
File("build/unzip_boot/generic_kernel_avb").writeBytes(bootSig16kData.readFully(blob1.encode().size until bootSig16kData.getLength())) .also { it.dumpDefault("sig.kernel") }
File("build/unzip_boot/kernel").copyTo(File("build/unzip_boot/generic_kernel.img"), true) val gkiAvbData = bootSig16kData.readFully(blob1.encode().size until bootSig16kData.getLength())
System.setProperty("more", "build/unzip_boot") File("${workDir}kernel.img").let { gki ->
Avb.verify(blob2, "generic_kernel_avb") File("${workDir}kernel").copyTo(gki)
System.setProperty("more", workDir)
Avb.verify(blob2, Dumpling(gkiAvbData))
gki.delete()
}
log.info(blob1.auxBlob!!.hashDescriptors[0].partition_name) log.info(blob1.auxBlob!!.hashDescriptors[0].partition_name)
log.info(blob2.auxBlob!!.hashDescriptors[0].partition_name) log.info(blob2.auxBlob!!.hashDescriptors[0].partition_name)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
@ -329,14 +338,41 @@ data class BootV3(
it.addRule() it.addRule()
} }
if (this.info.signatureSize > 0) { if (this.info.signatureSize > 0) {
it.addRow("boot signature", this.bootSignature.file) it.addRow("GKI signature 1.0", this.bootSignature.file)
Avb.getJsonFileName(this.bootSignature.file).let { jsFile -> File(Avb.getJsonFileName(this.bootSignature.file)).let { jsFile ->
it.addRow("\\-- decoded boot signature", if (File(jsFile).exists()) jsFile else "N/A") it.addRow("\\-- decoded boot signature", if (jsFile.exists()) jsFile.path else "N/A")
if (jsFile.exists()) {
it.addRow("\\------ signing key", Avb.inspectKey(mapper.readValue(jsFile, AVBInfo::class.java)))
}
} }
it.addRule() it.addRule()
} }
//GKI signature 2.0
File(Avb.getJsonFileName("sig.boot")).let { jsonFile ->
if (jsonFile.exists()) {
it.addRow("GKI signature 2.0", this.bootSignature.file)
it.addRow("\\-- boot", jsonFile.path)
it.addRow("\\------ signing key", Avb.inspectKey(mapper.readValue(jsonFile, AVBInfo::class.java)))
}
}
File(Avb.getJsonFileName("sig.kernel")).let { jsonFile ->
if (jsonFile.exists()) {
val readBackAvb = mapper.readValue(jsonFile, AVBInfo::class.java)
it.addRow("\\-- kernel", jsonFile.path)
it.addRow("\\------ signing key", Avb.inspectKey(readBackAvb))
it.addRule()
}
}
//AVB info
Avb.getJsonFileName(info.output).let { jsonFile -> Avb.getJsonFileName(info.output).let { jsonFile ->
it.addRow("AVB info", if (File(jsonFile).exists()) jsonFile else "NONE") it.addRow("AVB info", if (File(jsonFile).exists()) jsonFile else "NONE")
if (File(jsonFile).exists()) {
mapper.readValue(File(jsonFile), AVBInfo::class.java).let { ai ->
it.addRow("\\------ signing key", Avb.inspectKey(ai))
}
}
} }
it.addRule() it.addRule()
it it

@ -21,7 +21,7 @@ import cfig.utils.EnvironmentVerifier
import cfig.bootimg.Common.Companion.deleleIfExists import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Signer import cfig.bootimg.Signer
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import cfig.packable.VBMetaParser import cfig.packable.VBMetaParser
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import de.vandermeer.asciitable.AsciiTable import de.vandermeer.asciitable.AsciiTable
@ -156,6 +156,7 @@ data class VendorBoot(
companion object { companion object {
private val log = LoggerFactory.getLogger(VendorBoot::class.java) private val log = LoggerFactory.getLogger(VendorBoot::class.java)
private val workDir = Helper.prop("workDir") private val workDir = Helper.prop("workDir")
private val mapper = ObjectMapper()
fun parse(fileName: String): VendorBoot { fun parse(fileName: String): VendorBoot {
val ret = VendorBoot() val ret = VendorBoot()
FileInputStream(fileName).use { fis -> FileInputStream(fileName).use { fis ->
@ -373,7 +374,7 @@ data class VendorBoot(
fun extractVBMeta(): VendorBoot { fun extractVBMeta(): VendorBoot {
try { try {
AVBInfo.parseFrom(DataSrc(info.output)).dumpDefault(info.output) AVBInfo.parseFrom(Dumpling(info.output)).dumpDefault(info.output)
} catch (e: Exception) { } catch (e: Exception) {
log.error("extraceVBMeta(): $e") log.error("extraceVBMeta(): $e")
} }
@ -413,8 +414,15 @@ data class VendorBoot(
it.addRow("bootconfig", this.bootconfig.file) it.addRow("bootconfig", this.bootconfig.file)
} }
it.addRule() it.addRule()
Avb.getJsonFileName(info.output).let { jsonFile ->
it.addRow("AVB info", Avb.getJsonFileName(info.output)) it.addRow("AVB info", Avb.getJsonFileName(info.output))
if (File(jsonFile).exists()) {
mapper.readValue(File(jsonFile), AVBInfo::class.java).let { ai ->
it.addRow("\\-- signing key", Avb.inspectKey(ai))
}
}
it.addRule() it.addRule()
}
it it
} }
val tabVBMeta = AsciiTable().let { val tabVBMeta = AsciiTable().let {

@ -17,6 +17,7 @@ package cc.cfig.droid.ota
import cc.cfig.io.Struct import cc.cfig.io.Struct
import cfig.helper.CryptoHelper.Hasher import cfig.helper.CryptoHelper.Hasher
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Dumpling
import chromeos_update_engine.UpdateMetadata import chromeos_update_engine.UpdateMetadata
import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
@ -88,10 +89,10 @@ class Payload {
} }
val calcMetadataHash = val calcMetadataHash =
Hasher.hash(inFileName, listOf(Pair(0L, ret.metaSize.toLong())), "sha-256") Hasher.hash(Dumpling(inFileName), listOf(Pair(0L, ret.metaSize.toLong())), "sha-256")
log.info("calc meta hash: " + Helper.toHexString(calcMetadataHash)) log.info("calc meta hash: " + Helper.toHexString(calcMetadataHash))
val calcPayloadHash = Hasher.hash( val calcPayloadHash = Hasher.hash(
inFileName, listOf( Dumpling(inFileName), listOf(
Pair(0L, ret.metaSize.toLong()), Pair(0L, ret.metaSize.toLong()),
Pair(ret.metaSize.toLong() + ret.header.metaSigLen, ret.manifest.signaturesOffset) Pair(ret.metaSize.toLong() + ret.header.metaSigLen, ret.manifest.signaturesOffset)
), "sha-256" ), "sha-256"

@ -17,7 +17,7 @@ package cfig.packable
import avb.AVBInfo import avb.AVBInfo
import cfig.Avb import cfig.Avb
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import cfig.helper.Helper.Companion.check_call import cfig.helper.Helper.Companion.check_call
import cfig.helper.Helper.Companion.check_output import cfig.helper.Helper.Companion.check_output
import cfig.helper.Helper.Companion.deleteIfExists import cfig.helper.Helper.Companion.deleteIfExists
@ -68,8 +68,8 @@ interface IPackable {
// invoked solely by reflection // invoked solely by reflection
fun `@verify`(fileName: String) { fun `@verify`(fileName: String) {
val ai = AVBInfo.parseFrom(DataSrc(fileName)).dumpDefault(fileName) val ai = AVBInfo.parseFrom(Dumpling(fileName)).dumpDefault(fileName)
Avb.verify(ai, fileName) Avb.verify(ai, Dumpling(fileName, fileName))
} }
fun clear() { fun clear() {

@ -15,10 +15,13 @@
package cfig.packable package cfig.packable
import avb.AVBInfo import avb.AVBInfo
import avb.alg.Algorithms
import cfig.Avb import cfig.Avb
import cfig.helper.CryptoHelper
import cfig.helper.Dumpling
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.DataSrc
import cfig.helper.Helper.Companion.deleteIfExists import cfig.helper.Helper.Companion.deleteIfExists
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File import java.io.File
@ -40,7 +43,8 @@ class VBMetaParser : IPackable {
it.mkdirs() it.mkdirs()
} }
} }
AVBInfo.parseFrom(DataSrc(fileName)).dumpDefault(fileName) val ai = AVBInfo.parseFrom(Dumpling(fileName)).dumpDefault(fileName)
log.info("Signing Key: " + Avb.inspectKey(ai))
} }
override fun pack(fileName: String) { override fun pack(fileName: String) {

@ -7,7 +7,7 @@ import cfig.bootimg.Common
import cfig.bootimg.Signer import cfig.bootimg.Signer
import cfig.bootimg.v3.VendorBoot import cfig.bootimg.v3.VendorBoot
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.Helper.DataSrc import cfig.helper.Dumpling
import cfig.packable.VBMetaParser import cfig.packable.VBMetaParser
import cfig.utils.DTC import cfig.utils.DTC
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
@ -156,7 +156,7 @@ class Dtbo(
fun extractVBMeta(): Dtbo { fun extractVBMeta(): Dtbo {
try { try {
AVBInfo.parseFrom(DataSrc(info.output)).dumpDefault(info.output) AVBInfo.parseFrom(Dumpling(info.output)).dumpDefault(info.output)
} catch (e: Exception) { } catch (e: Exception) {
log.error("extraceVBMeta(): $e") log.error("extraceVBMeta(): $e")
} }

@ -0,0 +1,64 @@
[{
"device" : "ADT-3",
"manufacturer" : "amlogic",
"algorithm" : "SHA256_RSA2048",
"pubk" : "AAAIAJHD1q2K7QEFh5MBfReSuk2oQ9vQLC1/kdSBfizd0iFTyNiVd57KUIjCzfN2uMJWiLtNUYa7gK+1TxMt8wkFZq61Mob563hmPl8FbNn2qb4+/g/DsaeZstuptD9L9pBVuIyUskl8hZ3GFO3Xn9BXW19NAhXYdi5qUxGsWsInRSqqASLxmV/zEQGFhhEVh9ZlCEyYukqaVaUunEDdkcAABRpcaT61QOww5QbZe8T8K/FgVxz1Mz4eF1plphSaah94TCG5Wc+RFeZ5QYATuBqsKPU7t9BAmnxXBbhAGiSkKUpI9Ln9N42MLvR9R+NBAiCZCoUOKPYBirqcyp48TZJiLuY4MgLbYr0mJ3OKgxUF6Q0MLPPrdKca/KagLkojDnshDTFmejPEWJS1b5snCI0lH3mr7HRVNfst4e8l4IfS5AxX2YLuAhWvjHql7Yvhdysy7toGZapQw6REjOgvleIUwI6QGBSHlr2PQ8zCgJFFk3dAo2EYuk3hjNEWRGkW8DB4l4ij2yMYI8w4vFVg5iacdfVL4lfgItQb6OYSJJZADOcCrHhqR+OWOLftyjFP/oSZC3T6I30F/PIBAb+i0FSm+np2yycv7T0GgxZQD3ESx1Vsm4QtXIy5E1ToLumXWUN2++yi30XM+wfqpargU0vNdG5pgWg2DR7SGcKjIkWqJ+LJ2B283A==",
"sha1" : "294dbb09016c013c57750edce0df0187409d56ce"
},
{
"device" : "GKI",
"manufacturer" : "google",
"algorithm" : "SHA256_RSA4096",
"pubk" : "AAAQAN3haYGQPHFa9yJAviIpp5Feoyn9fr9IEOXPfDzY+SzDr0drrCjO5gY59btYOt/RbVhiXRZK3uSwN3OSiQ98CtECYG9AxUu+3m7o0W+y/UiQkvbbLN+gnbZjiAqWWBIdT0RUTdIf98eMBdgzbdpbDn7uzmwDjLvCkrc/rsWcYYeD6VuS0qKiw/Bzm/8hp7lqqtW0fgn4Y7OBPXRfckyNExjkSvktZZKDFFKp/Xcio5coGLBqXc+1g0Lmo2osbx4bSP4cwJSxKtEeSfH51DLPSc8w+tba/8Jmp+j0KAf5jowNzRKyxyjxavzs0c0Us087MkHN4MaBeLIRH4twUtBRUVKNwacmC3vkFjgnVhpATzXHQa6OYQdV6I8Hu1GyKJ1mr85vEOroNB710Kr7r4Y6YhkW4TpYJQzQ0YZGVQXgp0q00qAQGa9BANrsBaPHFLfLMt/oxGWmIIEeJPKMJ20VTojJqC0XJp39Ujbn0UXC/5V9lU94BQYwog84FOVh2THwcMYsDFb/WlJkF6ERKDYjQwEcIPwR18E1oM3nBM3x5TJpeCV929gxBKYVdf3uoPwiq/tJmM6m+HcHrqjnwN17i1SyG54Hg5rXRQG98Zx+jh4mam1j6dD9dnm0UYNbU24Bca5QhfSR9gwTXxj+9owvyNhKFF41/LcEeaIl+4W0azzj0Mcpf45+UlP0vG5mxKpzlasQthS8m6nPdkYrKrSTClWueuZRhiW0lnPF7PCj8n9jccJ2xYoqcdKRja3hQuBp5u6cRjZi0ACimdTwAMjw7IVkyNlJsTEHPCcQCMknWp5sncJtqady+BbO4W4AfGqSdi6zEZQc3bP5QdQsrnT3rocNHoX3gNwh+hDaED5Rj3MzPXFclb0xfLf1yMSIVuGCePKAcOqwlVbQIeoo2yfHPWX02EvS1JbBrkBCAJ6HRw3e5t/JQXm+faSBaTWGrSBwqaG/ykXgEDzjdeTsGnVr5g9vazd3Z3Lr6F7HqTIYvMZ+ovkIwiBC6dapTX0Ow35m+NjYSTUd15igmaqxjxq4zq2wxE5T5DQhF5ria8cp+0AJhWkfl35oxPWPnLworlDjvu2BdEb0RtfkHM8/po1BBlHzYqqTyV1NqkQq9nO6gVCyGW/X8XufeXKg1CDUiGfjn3IhIIXVcmjW4NxAgdCZdAjGNqIyaYR/LOksw0uaB1IFh0VQNjj300LOXUbJMXdaxzx1sQpw+nFSNYtTX6Uck/XfeBJDSiae5kbVQcey0hhbj1CDWT/u05hVvtxbmmcqVcS+pXk3wjJ/nnWJNga+DYtzK678/u9Fcj0dC+fsWXlEgRen9i4hDynaB7nkOFsnrweA014TLwuSSOmdEDnO0NMnxeQH",
"sha1" : "144820003f9d46c96e9090dcf0e4feb84ca84810"
},
{
"device" : "Pixel 3",
"manufacturer" : "google",
"algorithm" : "SHA256_RSA2048",
"pubk" : "AAAIAH0QbcWadnydcrOvnaY8DvH/VQVZ4o2h/uUahVy2SY+E8krdSWU9Wcg0CZl/AvHPPL2rD1i6KoLDzqgS9cyybSJYH2LA+J7MIlBTV7+z0tMT+rYT7NkxcC+5AZjkQsmL+fbS1q0VIEtCM/ugPnOY1Imympq3rOxrGee8I/bNm5GxCT//2xUIjQn5D/1oQ6eCtBGBMgY4B2/r6ooUM8Dhp+xiuwxSs6iEoZuh5Ba/xWh6neRe7AHT54axGdXz50B8aQx3DXlvVjuz8LA9aUrlD9o6qYftNgg4hfaKPx5Vi0zw4teIL86F6Plte1F7HDgyjPDxYyAKsEk+PJPGexGJHeEPg3bzgRR3+ss18VcmMgL9Dk2afbuf2VANcxfNA5J7e4J3YGapPwILLMLlojIACk0b1gBclH1hffkQZ4O/SYC0AGsMJDI45kauUWJmYglONmqxgh+rDI5xQP1KiDkRDtBWjocGcf7mHlxvI0E7X+oK7V+cEFEbAK5sEvSs4QDGlb+bjFS+WRYpodO/HzF5M0Cinbn7izvek3uP+LMogj6ELEnZqe9u7ubmgKv/BIhQY/RiRx0goLkwXzVwaAp7y6vh/Vi6YB2pzR6IiItRCkoPK6U1xnVJY/oElv0icqDA2ynXbxZcc6D6OXqTcueKEmeuSGe7jan0y4oDnMLd3k5MaYn5iQ==",
"sha1" : "ad8569837cc06521720a35357475f87283b59234"
},
{
"device" : "Pixel 4",
"manufacturer" : "google",
"algorithm" : "SHA256_RSA4096",
"pubk" : "AAAQANeIn3+J2UI8Yc4LIB5gWoLlw4d/OHXZv0M5zfchR9JB/RNaGhW1ffN4AgsdQJ4vLBSdf4i5QNlzMTC7sQ5eFjKCGrAp20cht69eDM94UTmTEizWDTH3tGRNL0Z7qyQUA4p21QY0GCcsMYt/r3XY6OSpbSz9iqu6bByX6u1mZzv/Tbd4C5SyA8hHHuTth6/PqAUhKvkfzg8MhVfGYx2hVTvZlun5jnXJyZXX4tqbzE2hZbpSSdTG2LvvJ9fBCuqeTs9UQTVaRZsp07l4OKReIlHDZrcyt01Gq+HrvWtpnX8JxgBLSMtO88BAfS8sgTQjtE/OC5jcRbrr7b7WYVpwUjeUa5hxgEnMJbcWn3fE2RxWWTQUFJM99UTUuSQm6xygc2u7cBcphdnQdqHyrT+DL6WQHFxGo9wbhXuq4MChvOsilqyWmml3tjUYlfv8CqP88ltb56bFD3Igk1cyaLrIE01sPs7tdfrKQf8t4ljW6cAPSwf16VKKCR5dBx250S5ru76+dqdVk3h2XY3eAp2EjiSSSfFVW+ZyOy22+4b10pZCe1ruqdc6sUE0Y4QzgRGBy6Ek9MjXEijVjc+5X9ALS8ED6N0cgmWaclk/cw8AHQKCcBP3HlnccjK4dQXBOw97YolYSYNJFWljJUpm69TgFfyf2RyWeN2kCHqzRBDS105UOsjfgUf9yf8ubHKFITlyqhvMzl6dCBfUDgQgcMFccnMxXPEZy3g24m/kjyB9WiBI2KBn6SbC4BkiAOqjLB9f/pb1nn/6aILeY+iqirs77ucWkMz/H3Xlk5lCNos57g70r5gCBtjnTbWMFHs1m6X43v9iOsr3I4zPq4/QdEwpXnz9gBLz63n+kR/IT54ugiliYMdFRSyrTNyGd1L2mCczdxk5AapxVVZTQHdl3bMU1/Mu1YjGLh1F/ipug35VvlIfL+Y1Wded3vdFB0DAciG/8m0na6mQyfHYxEurt4Fq4ifkxMOP2NVh5CXOHCSbnHyqTDj5XvMT6fcnEEHE+Z25ALC4GI4kkHsMfoejdINmiJePTYDjCSgSBAoFCCul0HVeTH/VUBMotlf3LPhRuWGWuT3/Tk/yIR0JASSeCc03YUR0lsf1rCZEyWhPc1n1e6Rwlp8fCEa3J/Q7i/pHNXSWE5EJKlU8Jm4VH8J/oetSa+7NKgCeSUFtBcg00zg2YBfuam/dd53LM1FyJYRWV4ONWpL3Mhxi0TuSVb9j3BJL7R56wiMuuq/tVgsNzujhWdezYVo1oZTJrFj6LLdK4DHD51BK3erc62FlSGw7vcHFRVhgE8y6EJFcqKTVBhqJ0Qmr6XgTBA12BKArAjs4QG5/AwrDrImDqrB7udkAutzx2vHQ2ytR",
"sha1" : "8c44014b96f0f41f3daa3825d4af410233372b65"
},
{
"device" : "Pixel 5",
"manufacturer" : "google",
"algorithm" : "SHA256_RSA4096",
"pubk" : "AAAQAJQ75c2T8qdhsNTAlO37M/QNDoIIS7jUkIqOINM+UygqTj8/BVbIih31wOT7oglvXR+3baLR90iAqrTCBhhMrWPw6cdFE5IrpIho+4y7heWCc8VCkR4MBsygEHuseYtB0WJegHnI3YxNOx9HyDo3HuWFkYx8VNlOSNCep6Ce2Xbn1BYjOTIUeWOzee0UY0lLCyriFAf6YkLDmplI1jYBWs4R0TANTJQlMzOThratZXW39IDLuCmlX6r0Wg5ljT25tb767ZG2NHUTQJoLHV95iOyUwuKZa/hvUOmOM0JGVDcVXpzwUX0HN2Q5i4LBulxtQVFrol5yHO1jbK+el76FMhlgZHjEto6peUmZodwLzXybAgbakK3vF8V91DOy0dnkbyHiejzgMhPCCat6wNjhtYs0EMsOtS+WOzxdxFUZMp+gPQrT3f+5HTBkfpTNHdvhiyxZsM+Xqyi4O5OX8LSszWMEviq67OkQawgc9tbsNX6tCc0gH9EiYro/ecOcI/JA5yfJce5gJlIUvavgFzsi9DFQqHcnVevd/C0D6CderCwv5QuGqGfSkt3FJv6ITmFAG7IhYU4Y9A/OkLInZChiQ4nxL+c63K1dVFmNidwQpFwo0+49hvZ9VJ5HsL6px7Z167VAVOkReFjBbqOPYAeq0Xy3wZoCxQapuqxO+VUtR4H5S6xw+ygZzGpXfJJA0BNIdxsOEO3DRlv1lx4Sa6efHYBhodtelrayF19izHpHHRifFMwQgDz1mOU9AcM4cvpqYKIhFaH9d6wRMsBif0L+rT1LJh691bfLR5P5EyPhrb28+1/agYI6EIt83Aaz3d9SnamqAqyGR2E86o8HXGo8x5NAPdp7UM1yt7rOyhNwcG+dVj535XsS2hzHsaUn+PWWanLF/bSuoOYwCbLf22eCmE08uCZW2JHJdzRCwMXy3ZSGbYxOCfaMcaHOpl//4KcHZNBd4j07ODKXiE8rueB/hfMx2NMmxpkeCIf1dxQsFgnC3iHtixgMVRkxuQTAh7ePOu9yEgj17ImTFOVLm1BaBBnd7dDdg7bgiOMloH8+1HvPWerDnz8ghLE044nz73NSsixKvEAm8wAeWbbG8/uOjCsi5opYh1oBgdjUTQEjsu8CApbvjrh8//f8/46n+/SJgBB0AJKY+F9pRk0p2duX2a1R/zbpM4Fa06qjvwRycyGw8A4gdmwFt7189dYjYAukPoQ4qONJn7BXQMFM2EhNLOUyfdz8SmekEL5d9NVG2UB0m9mYdYCbwgPvCZQAsqH3i2pNslx/9GbRS59Q6dSqsDDk2TSFst/2biBiAyl7glFoN/bggoGESxPc3t43Mi/O/ym3l1qd7oQGav7wZL0J8iZ/I575",
"sha1" : "6908461c2ccd5568d1f39016a9da5aec5b10a785"
},
{
"device" : "Pixel 5a",
"manufacturer" : "google",
"algorithm" : "SHA256_RSA4096",
"pubk" : "AAAQAP2EZ6+lhRYjp4cE4WW11okXlC20K5OJWj7iX0bmAGcmPMtWNJt3Jq/w+5ZvfRMa5xy3CMZeJICV1XeD7R/Pwcs9Wadd+DR+Lsai209qJiuMv6R4Y+eF+EacD7NunRpDdaDGnTxdaWJUbYDSFDrwLO1PHBavewd3ujVCmuh1zaQ2CW3Y1lo0UvmO0tISRr4yvR5Ia+zaufQ0TtmlWwX6se7jJ0/ap9Z4wQYJS9R8WDRqV+XX9QUDuOU/w6TI4hB9oYDjiysCtzJe9zibK6jSNq1/3CfOm4GuaCLQEJseoqgsNh25MkTqg4rmmPsk+5Kcylr2gDFRmu4NOJ/sCRIVDXkigrz2WDaB3UaBTN/JZMjuFvxk6LAQSURE4oFuljzfTvnp2bIYbqZ8NUTHuzhEDwelj0eChAkW9hQ85OBTs23lJmAYQxJ+WqYixrB1lpmgz7MUzc/2PJBXLh96FdLJcU2jCzCfrAAepELWsutPfVcE8rOFAdrAF6PPeHUN87pfrbGZx/oj1Nkf0kOic77dqHERqWw//7WngbzHPahjyX33OheZK+xK5tzx2EUX09yXMK84d25UipJuHQ0SsBMM3EmI1b9U2IDEHfhIg5QpJFO5xF1DQyLTu5lG9mAqJcGAjnIt47Jkv3s5i6zBlmXWJJicgnnbDjN7sn/w4LiZY2Uu8ZSwsSobI5EDG5vIdD7NlueFB5zDoqhm19eG2z/QfywOdFlgsfYPDmg1JikeWnpDb3Yy5xPb1ua5TFYItduid0aM+VkoL/KbPt2oVgh4O2uZTFebUHMMCnyGw2zwfjeh4xrD9mD5xWdcjo3KYB1nuv/GtkNEDXpsUxIII4E3ipjfnNHJBz0UPlYnuxDB34KpvHoa/GG8wEWJCDcL0dZX5/KaaiTHwlypWUXnSi3rlm+qcMivO14f8LFmmabMEetiA2YIVqtIXB4MCIYKYr5aF6879LpHA2bFI/LXyGf6pL0tM1CAiQXZwNc+WHC24ujCngT/QD2nRdqWDKuwycG/DtRJVnn+Z5WVD28PZotv/6ODT/4f1OLgM9ufq38Vm2SETZNYCZr4Zcn5bgR7ObqyTnD+vCcpfTDifVp8xPlHT1iY7gdb0gSV8ZIIXpcxUB0kBpM6jO2HJST9hVOBQfMo3V5xjBJYkFwcrCI0bC58zf344C145YuUKchByC5NCkJXHe7YcD9Hut2fIxcZqO08w5XDgJucfS6hMOaspt7XhibxwsE/xF33pgqRz9kFOJ6DnjhGbvbWo3VfNpGdeBfu8vmSpNKI3plaxImWEaK360KHCBVgwqLrhoIgf91FdT6MSO7gGVOHbB+o41UZ8mjE71yrIWA4Opp4/qpVUJhVR0aR6z8d",
"sha1" : "14e3fd68feb4fe7c9837787ed218d083050574da"
},
{
"device" : "Pixel 6",
"manufacturer" : "google",
"algorithm" : "SHA256_RSA4096",
"pubk" : "AAAQAPzSq6nF6uIU7cpTp90ATW+SFDH+L1Rv8NBew/USm5nNdFNhm+HXbWwz6cADn/dtUEd+eksUc6uxisT4EwziErAMbgYyN81+W1ndYFhp4zU1C0YhlS+LnlPxY+rNPmShpFvMIvMCH8GK24tO50FjjTBZrz5kwGfJt0kF7jQwZKQr6e3PMaKYZH11jeAdasEoxwsMjX31U74N8T4r92H8caaLzGJm2qaXn2Nec1HIWwUn7GSigG9yDf6RSzlc6PhKYZvcNG8AmiqghBxuN9HrBc4zo5z2IIm0+W2zxzoPkoRQrKR5LLRoBAifw9zO/xTiiuz/LcDjOesFrdk/vWKH1BJN3bx2etaH7qpqqQo3gjfc//VBZrZu1GelxL1KBNYQ+somh1NGUUKm3WDHtDBzMOrCph4zUyKLU5sqQIzVaqk3mjD4QpLM3nhsuZfheSDEFv38fmX34w0Y34bdogOqM7qsWOAEkxX4SugVrEakxuNoTDaXi765vxnrXcsgyWaalMZpsNra9AJujXATlfvBSRLqd/++B83voe41rnCLJUR3TRIy5PQiE7Zvpv2HZn0ix7EVg0ZRVRKBjubcAVFrtD8pXStqA5v6Jt4sy4g8QKHecgmKj3oad37ubWFDEQIRiEYceAA1xYRwCbMrJoFaBysNUuYp4a68FeZiRqbIg5pxC8LXZ5WfbGOcq4xzXkqk327J/1UxdTLvQT/HKkcefRPbrCh7yEFYSn+XCNCx+w+/GdYyKJ6X3sYPP6Ko4R2O+N7FC7SPfn7qfl9yIOeRcvX6EHaXXMfA4PG8EW7dNZvPXa5Sp1g79w2aFEIRWolj6czvgPfC0fHvflFrd60t1K9WiqxvcQFG5spOUrhqltobGjHo6FS7ohWiX5sE9mJxUZap7AR7avAkAWibFBSyv59uxt4KzXZGqsZJw2vuEWeImXAIc14xSkLLJgeoI0ZIfxqrs+sctno4HMwr7tdzMKpTS8Ni/sJSVplmSzdTJ01V74uDLC7VreL+rjXvYLcBV9363/knhX0Os+O01aTSVwGsYBbKwpEuiqonS4yNs5G2NydQvHoDaXUNAzztMd/UmC4WHZOV+vM/daqXAniafJx3lXi+Zgxw3nl/vU2mgNNek2U1Y5xmefhKjBwVhqmIzZY4Lab6Cobs7sEmXrwOe+578PjnFzzs0MDEaJd6vJExVOe5pLvWhUk4vBFb0eK8JcbY65rXWfOJhm0DKlMd7yIidIQaYK0drCNhCM4Fr33YJmQcFBo2QgLsmESnayMJgmPU/KhK3BwZU0OdUJbsGupzvoJIGdnFrp+0oTxO/X4Mr5ArUmoMSFx+rq6x0sMBJNt/kxvvFKcETHN9oRn15V8pRpDl",
"sha1" : "85322346680a860c091fa14a64cef1fe4a3ffe31"
},
{
"device" : "Pixel 6pro",
"manufacturer" : "google",
"algorithm" : "SHA256_RSA4096",
"pubk" : "AAAQAAw4tB+0tXQVo8gxwKDukkqG5UfUBSsSy/Y46e/wBitpPv6EwpFsTzg8Jw+Xlgw1wXDJPk0nWnAWk2CrxI//64nVRiOMYquuL4PlUyB0JHkeDH9+2EH6YsOZABRUHkj8KvKovqFWxjXyS7pGbFTYxrZn2f04A1GfYJK9sG0TvW2CvuJs2/MgflXLie4N8V1hpjiuP5Qsu7xEzh/uJrzWDBKLEdgC8FjkxufHK366KaYqfrUuuX/bgQpO5MfzynFaf+PysG3YR2+JN2rnd4hpgqFt73qILm8EQ6Ls4gWijloA6M+MV4QW+jMtKjKhBCNITPLSjZx/0GgZNN5MS2/7kq4dNgmjBCP5qFL8V1xa+r1sdDG2WA3n5uEnW4WxddHgrTVg0F6owpHyaQ28uyNFRwKbUK5td7rS5RDFOS4f3Qpy9Gp0VAFXVylxUtpzJqU8e4UkgfdXDgOVcZrH80NtGasEKgZjvuvTVxv4WBVsyOqIxwv/rpMKm/zNUXxRLeX57UFRfhHTXi/Oyjz+ImGFeMSuv3vS3tIE1bH89sKDL9u6KshjqClK5Gv0p47XSSMaHzh2WDG7SM4Lf3zuqwOv1v1iN2QECaYeBDRuroMItmpqftP4D09p3SylEnK8DeJ8MHPFJHbjfMCo0S1GIs6maKNycYKr2SBKPGnjFaPpFqPLIXY4IU5bEKur+soemPDVylpRL2wV4UnUdhMgQAaESRTNqln4rIyNUCZRJPROshq5F4R8dX7Iy+XIb1B1ctI/njVNP+87qirrJmol71qnRPRSJ02SXUb48/cAjXVKqb678/9MoTwjW/t0XXDWPWJFnXC/zc0gATFz8oZdLtnizCkjeXZkkM4v916lvcQ2Zha2GXT2ZC11NU/K20rYjgtyRg2RT3MRfgwtwymjGMl3lI4PG4m0DL63foVpBvG6TDnIp2FJXbBoBZLfaSquP1A8Yqr0fcMfRPeWiTCfCiKIaXVN09SAsJA/ZB+VjeC3hX5hO/PMDfYBzgaJFOCfpLCqaEVZi0MKPgFoXcCc+wyIt2aW5LyplUEB7DS4BKnckZM1SP6MJZfBBalEcpbsB0g2koZHBDtC6I0C0F7pEJedATUCG8jHNha8wOTKZf2+I/nvJP7UMER+kjADHuyqr6TFvSbch3H7zOqT+oVPaxTqLA8tQ45cTNjeGPLb4CghfHrvKqDqQDkpF+w0zBLFbrzs0Ab3OKfaF7nhVPn7Qp/g41pF3Xo7pv/HVXwIL6WnbQhyJmQsdxFnVjndzFzkNwoL+it2v/cBXhAFbacm385E44DEZNTSehxgofGn5Y4ShVeVd2t3hC167vpBMsyxf9ilvn0jYeA8CTK7+ec2H0jrCBNyBxY5",
"sha1" : "5f9cdcb2a3e84c93eb031529eb7b3a05b1887861"
},
{
"device" : "Xiaomi Phone",
"manufacturer" : "Mi",
"algorithm" : "SHA256_RSA2048",
"pubk" : "AAAIAN3C1onuaVOoEOIIpxRB4slj0xuKT7t2yrvDzvHQhOAlM8vz+rnR//6imJ+ET07/dMvAE893FM5LnFQLNcUJ+FYIGjbYdzFCVSYErfVTq96e37/sxVT86Zuz392q8OBkY4UFzpNdwwzWq6J+0tAsEyu778ECITiPceTJgol7IxzjIljHccK3ffP9dQf1FziB6BBTOQBKj26J2KCqtSmXM3Kk4GeV0wdCMZUUcuWXGrt0UjmrD+xHa8jSj9+YRb7BWe+RfYCXtqrAaree/Q24GAm4nSznDjfSH4T50JiNBkaeOf9YZwQlh67JtFZbueUP5prlQ3eXmubddodnA0LdDkOJooBHvMZAM/73+AWtsf145vMRUdtuvj5Dp9shR+kHaBoB78kQ2vUcrT7eetQynrH1TtwpXzXqAfzHRfv1tw2icvPnjFYV7m3njNcrb05Bx/oMvqKzrx99kCZEWuVm8un3gC/ucW9cXkAB+onTQnfJbeS5XcbEPcZkuQ0b7BepCScYnMrlOMSClYYl8OPNK576YRa2KQxdWEbdukihNIhaZhBN/tqdszA2w2960rO70ektljBVPP6m2jcT5iV0spMiers5KtqZ8cy++Urhvg0mWiuPcnue4L7W59lf9bJVlkaFrBfOHvxMdgmmrHCgZd9278zJrYDgFK17kojwSqqqwCkoFQ==",
"sha1" : "b2a02f1e56e366d727a1a8e089762fe0b91bbc84"
}
]

@ -234,30 +234,14 @@ class CryptoHelper {
return MessageDigest.getInstance("SHA-256").digest(inData) return MessageDigest.getInstance("SHA-256").digest(inData)
} }
//fun hash(file: String, algorithm: String): ByteArray { fun hash(ds: Dumpling<*>, coordinates: List<Pair<Long, Long>>, algorithm: String): ByteArray {
// val md = MessageDigest.getInstance(algorithm)
// FileInputStream(file).use { fis ->
// val buffer = ByteArray(1024 * 1024)
// while (true) {
// val bytesRead = fis.read(buffer)
// if (bytesRead <= 0) break
// md.update(buffer, 0, bytesRead)
// }
// }
// return md.digest()
//}
fun hash(file: String, algorithm: String): ByteArray {
return hash(file, listOf(Pair(0, File(file).length())), algorithm)
}
fun hash(file: String, coordinates: List<Pair<Long, Long>>, algorithm: String): ByteArray {
require(coordinates.isNotEmpty()) require(coordinates.isNotEmpty())
coordinates.forEach { coordinates.forEach {
require(it.first >= 0 && it.second > 0) require(it.first >= 0 && it.second > 0)
} }
return MessageDigest.getInstance(algorithm).let { md -> return MessageDigest.getInstance(algorithm).let { md ->
coordinates.forEach { coordinate -> coordinates.forEach { coordinate ->
FileInputStream(file).use { fis -> ds.getInputStream().use { fis ->
fis.skip(coordinate.first) fis.skip(coordinate.first)
val ibs = 1024 * 1024 val ibs = 1024 * 1024
val buffer = ByteArray(ibs) val buffer = ByteArray(ibs)
@ -280,6 +264,14 @@ class CryptoHelper {
md md
}.digest() }.digest()
} }
fun hash(file: String, algorithm: String): ByteArray {
return hash(Dumpling(file), listOf(Pair(0, File(file).length())), algorithm)
}
fun hash(ds: Dumpling<*>, algorithm: String): ByteArray {
return hash(ds, listOf(Pair(0, ds.getLength())), algorithm)
}
} }
} }

@ -0,0 +1,93 @@
package cfig.helper
import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
class Dumpling<in T>(private val filling: T, private val label: String? = null) {
fun getLabel(): String {
return label ?: getName()
}
fun getName(): String {
return if (filling is String) "FILE:$filling" else "ByteArray"
}
fun getInputStream(): InputStream {
return when (filling) {
is String -> {
FileInputStream(filling)
}
is ByteArray -> {
ByteArrayInputStream(filling)
}
else -> {
throw IllegalArgumentException("type ${filling!!::class} is not supported")
}
}
}
fun getLength(): Long {
return when (filling) {
is String -> {
File(filling).length()
}
is ByteArray -> {
filling.size.toLong()
}
else -> {
throw IllegalArgumentException("type ${filling!!::class} is not supported")
}
}
}
@Throws(IllegalArgumentException::class)
fun readFully(range: LongRange): ByteArray {
when (filling) {
is String -> {
return ByteArray(range.count()).apply {
FileInputStream(filling).use { fis ->
fis.skip(range.first)
fis.read(this)
}
}
}
is ByteArray -> {
return filling.sliceArray(range.first.toInt()..range.last.toInt())
}
else -> {
throw IllegalArgumentException("type ${filling!!::class} is not supported")
}
}
}
@Throws(IllegalArgumentException::class)
fun readFully(loc: Pair<Long, Int>): ByteArray {
when (filling) {
is String -> {
return ByteArray(loc.second).apply {
FileInputStream(filling).use { fis ->
if (loc.first < 0) {
fis.skip(getLength() + loc.first)
} else {
fis.skip(loc.first)
}
fis.read(this)
}
}
}
is ByteArray -> {
val subRangeStart = if (loc.first < 0) {
(getLength() + loc.first).toInt()
} else {
loc.first.toInt()
}
return filling.sliceArray(subRangeStart until (subRangeStart + loc.second).toInt())
}
else -> {
throw IllegalArgumentException("type ${filling!!::class} is not supported")
}
}
}
}

@ -38,75 +38,6 @@ class Helper {
var dumpFile: String var dumpFile: String
) )
class DataSrc<in T>(private val incoming: T) {
fun getName(): String {
return if (incoming is String) "FILE:$incoming" else "data"
}
fun getLength(): Long {
return when (incoming) {
is String -> {
File(incoming).length()
}
is ByteArray -> {
incoming.size.toLong()
}
else -> {
throw IllegalArgumentException("type ${incoming!!::class} is not supported")
}
}
}
@Throws(IllegalArgumentException::class)
fun readFully(range: LongRange): ByteArray {
when (incoming) {
is String -> {
return ByteArray(range.count()).apply {
FileInputStream(incoming).use { fis ->
fis.skip(range.first)
fis.read(this)
}
}
}
is ByteArray -> {
return incoming.sliceArray(range.first.toInt()..range.last.toInt())
}
else -> {
throw IllegalArgumentException("type ${incoming!!::class} is not supported")
}
}
}
@Throws(IllegalArgumentException::class)
fun readFully(loc: Pair<Long, Int>): ByteArray {
when (incoming) {
is String -> {
return ByteArray(loc.second).apply {
FileInputStream(incoming).use { fis ->
if (loc.first < 0) {
fis.skip(getLength() + loc.first)
} else {
fis.skip(loc.first)
}
fis.read(this)
}
}
}
is ByteArray -> {
val subRangeStart = if (loc.first < 0) {
(getLength() + loc.first).toInt()
} else {
loc.first.toInt()
}
return incoming.sliceArray(subRangeStart until (subRangeStart + loc.second).toInt())
}
else -> {
throw IllegalArgumentException("type ${incoming!!::class} is not supported")
}
}
}
}
companion object { companion object {
private val gcfg: Properties = Properties().apply { private val gcfg: Properties = Properties().apply {
load(Helper::class.java.classLoader.getResourceAsStream("general.cfg")) load(Helper::class.java.classLoader.getResourceAsStream("general.cfg"))

@ -352,6 +352,11 @@ class ZipHelper {
} }
} }
fun isAndroidCpio(compressedFile: String): Boolean {
return Dumpling(compressedFile).readFully(0L..5)
.contentEquals(byteArrayOf(0x30, 0x37, 0x30, 0x37, 0x30, 0x31))
}
fun isGZ(compressedFile: String): Boolean { fun isGZ(compressedFile: String): Boolean {
return try { return try {
FileInputStream(compressedFile).use { fis -> FileInputStream(compressedFile).use { fis ->

@ -1,7 +1,6 @@
package cfig.helper package cfig.helper
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test
import java.io.File import java.io.File
class CryptoHelperTest { class CryptoHelperTest {
@ -35,7 +34,7 @@ class CryptoHelperTest {
fun geographicalHash() { fun geographicalHash() {
val f = "/home/work/boot/payload.bin" val f = "/home/work/boot/payload.bin"
val dg = CryptoHelper.Hasher.hash(f, listOf(Pair(0, 1862657060)), "sha-256") val dg = CryptoHelper.Hasher.hash(Dumpling(f), listOf(Pair(0, 1862657060)), "sha-256")
println(Helper.toHexString(dg)) println(Helper.toHexString(dg))
} }
} }

@ -54,9 +54,9 @@ class ZipHelperTest {
@Test @Test
fun testDataSrc() { fun testDataSrc() {
if (File("/proc/cpuinfo").exists()) { if (File("/proc/cpuinfo").exists()) {
val ds1 = Helper.DataSrc("/proc/cpuinfo") val ds1 = Dumpling("/proc/cpuinfo")
Assert.assertTrue(ds1.readFully(0L..31).contentEquals(ds1.readFully(Pair(0, 32)))) Assert.assertTrue(ds1.readFully(0L..31).contentEquals(ds1.readFully(Pair(0, 32))))
val d2 = Helper.DataSrc(ds1.readFully(0L..31)) val d2 = Dumpling(ds1.readFully(0L..31))
Assert.assertTrue(d2.readFully(0..15L).contentEquals(d2.readFully(Pair(0, 16)))) Assert.assertTrue(d2.readFully(0..15L).contentEquals(d2.readFully(Pair(0, 16))))
} }
} }

Loading…
Cancel
Save