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
build/
local.properties
__pycache__

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

@ -24,7 +24,8 @@ import avb.desc.HashDescriptor
import cfig.helper.CryptoHelper
import cfig.helper.Helper
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 org.apache.commons.codec.binary.Hex
import org.apache.commons.exec.CommandLine
@ -205,7 +206,7 @@ class Avb {
it.auxBlob!!.hashDescriptors.get(0).partition_name
}
//read hashDescriptor from image
val newHashDesc = AVBInfo.parseFrom(DataSrc("$fileName.signed"))
val newHashDesc = AVBInfo.parseFrom(Dumpling("$fileName.signed"))
check(newHashDesc.auxBlob!!.hashDescriptors.size == 1)
var seq = -1 //means not found
//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 localParent = parent.ifEmpty { image_file }
val localParent = parent.ifEmpty { dp.getLabel() }
//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
val vbOffset = ai.footer?.vbMetaOffset ?: 0
//@formatter:off
val rawAuxBlob = DataSrc(image_file).readFully(
Pair(vbOffset + Header.SIZE + ai.header!!.authentication_data_block_size,
ai.header!!.auxiliary_data_block_size.toInt()))
val rawAuxBlob = dp.readFully(
Pair(vbOffset + Header.SIZE + ai.header!!.authentication_data_block_size,
ai.header!!.auxiliary_data_block_size.toInt()))
//@formatter:on
//integrity check
val declaredAlg = Algorithms.get(ai.header!!.algorithm_type)
if (declaredAlg!!.public_key_num_bytes > 0) {
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("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")
}
inspectKey(ai)
val calcHash =
Helper.join(declaredAlg.padding, AuthBlob.calcHash(rawHeaderBlob, rawAuxBlob, declaredAlg.name))
val readHash = Helper.join(declaredAlg.padding, Helper.fromHexString(ai.authBlob!!.hash!!))
@ -293,7 +284,7 @@ class Avb {
ai.auxBlob!!.chainPartitionDescriptors.forEach {
val vRet = it.verify(
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) {
log.info("VERIFY($localParent->Chain[${it.partition_name}]): " + "PASS")
@ -307,7 +298,7 @@ class Avb {
ai.auxBlob!!.hashDescriptors.forEach {
val vRet = it.verify(
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) {
log.info("VERIFY($localParent->HashDescriptor[${it.partition_name}]): ${it.hash_algorithm} " + "PASS")
@ -321,7 +312,7 @@ class Avb {
ai.auxBlob!!.hashTreeDescriptors.forEach {
val vRet = it.verify(
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) {
log.info("VERIFY($localParent->HashTreeDescriptor[${it.partition_name}]): ${it.hash_algorithm} " + "PASS")
@ -334,5 +325,51 @@ class Avb {
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,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa2048.pem")
defaultKey = "aosp/avb/data/testkey_rsa2048.pem")
val SHA256_RSA4096 = Algorithm(
name = "SHA256_RSA4096",
@ -66,7 +66,7 @@ class Algorithms {
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20)
),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa4096.pem"
defaultKey = "aosp/avb/data/testkey_rsa4096.pem"
)
val SHA256_RSA8192 = Algorithm(
@ -83,7 +83,7 @@ class Algorithms {
intArrayOf(0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa8192.pem")
defaultKey = "aosp/avb/data/testkey_rsa8192.pem")
val SHA512_RSA2048 = Algorithm(
name = "SHA512_RSA2048",
@ -99,7 +99,7 @@ class Algorithms {
intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa2048.pem")
defaultKey = "aosp/avb/data/testkey_rsa2048.pem")
val SHA512_RSA4096 = Algorithm(
name = "SHA512_RSA4096",
@ -115,7 +115,7 @@ class Algorithms {
intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa4096.pem")
defaultKey = "aosp/avb/data/testkey_rsa4096.pem")
val SHA512_RSA8192 = Algorithm(
name = "SHA512_RSA8192",
@ -132,7 +132,7 @@ class Algorithms {
intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40)),
defaultKey = "aosp/avb/avb_test_data/testkey_rsa8192.pem")
defaultKey = "aosp/avb/data/testkey_rsa8192.pem")
algMap[NONE.name] = NONE

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

@ -17,7 +17,7 @@ package avb.desc
import avb.blob.Header
import cfig.helper.CryptoHelper
import cfig.helper.Helper
import cfig.helper.Helper.DataSrc
import cfig.helper.Dumpling
import cc.cfig.io.Struct
import org.slf4j.LoggerFactory
import java.io.*
@ -113,7 +113,7 @@ class HashTreeDescriptor(
for (item in fileNames) {
if (File(item).exists()) {
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 diskHtHash = CryptoHelper.Hasher.sha256(readTree)
if (!ourHtHash.contentEquals(diskHtHash)) {

@ -160,6 +160,14 @@ class Common {
ZipHelper.lz4cat(s.dumpFile + ".lz4", s.dumpFile)
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 -> {
throw IllegalArgumentException("ramdisk is in unknown format")
}
@ -273,6 +281,11 @@ class Common {
AndroidCpio().pack(root, f, "${f}_filelist.txt")
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 -> {
throw IllegalArgumentException("$ramdiskGz is not supported")
}

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

@ -21,7 +21,7 @@ import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Signer
import cfig.bootimg.v3.VendorBoot
import cfig.helper.Helper
import cfig.helper.Helper.DataSrc
import cfig.helper.Dumpling
import cfig.packable.VBMetaParser
import cfig.utils.EnvironmentVerifier
import com.fasterxml.jackson.databind.ObjectMapper
@ -245,7 +245,7 @@ data class BootV2Dialects(
fun extractVBMeta(): BootV2Dialects {
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()) {
log.warn("Found vbmeta.img, parsing ...")
VBMetaParser().unpack("vbmeta.img")

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

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

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

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

@ -15,10 +15,13 @@
package cfig.packable
import avb.AVBInfo
import avb.alg.Algorithms
import cfig.Avb
import cfig.helper.CryptoHelper
import cfig.helper.Dumpling
import cfig.helper.Helper
import cfig.helper.Helper.DataSrc
import cfig.helper.Helper.Companion.deleteIfExists
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.LoggerFactory
import java.io.File
@ -40,7 +43,8 @@ class VBMetaParser : IPackable {
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) {

@ -7,7 +7,7 @@ import cfig.bootimg.Common
import cfig.bootimg.Signer
import cfig.bootimg.v3.VendorBoot
import cfig.helper.Helper
import cfig.helper.Helper.DataSrc
import cfig.helper.Dumpling
import cfig.packable.VBMetaParser
import cfig.utils.DTC
import com.fasterxml.jackson.databind.ObjectMapper
@ -156,7 +156,7 @@ class Dtbo(
fun extractVBMeta(): Dtbo {
try {
AVBInfo.parseFrom(DataSrc(info.output)).dumpDefault(info.output)
AVBInfo.parseFrom(Dumpling(info.output)).dumpDefault(info.output)
} catch (e: Exception) {
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)
}
//fun hash(file: String, 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 {
fun hash(ds: Dumpling<*>, coordinates: List<Pair<Long, Long>>, algorithm: String): ByteArray {
require(coordinates.isNotEmpty())
coordinates.forEach {
require(it.first >= 0 && it.second > 0)
}
return MessageDigest.getInstance(algorithm).let { md ->
coordinates.forEach { coordinate ->
FileInputStream(file).use { fis ->
ds.getInputStream().use { fis ->
fis.skip(coordinate.first)
val ibs = 1024 * 1024
val buffer = ByteArray(ibs)
@ -280,6 +264,14 @@ class CryptoHelper {
md
}.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
)
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 {
private val gcfg: Properties = Properties().apply {
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 {
return try {
FileInputStream(compressedFile).use { fis ->

@ -1,7 +1,6 @@
package cfig.helper
import org.junit.Assert.assertEquals
import org.junit.Test
import java.io.File
class CryptoHelperTest {
@ -35,7 +34,7 @@ class CryptoHelperTest {
fun geographicalHash() {
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))
}
}

@ -54,9 +54,9 @@ class ZipHelperTest {
@Test
fun testDataSrc() {
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))))
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))))
}
}

Loading…
Cancel
Save