From 067cab9406bb5b000efad50ae81f9b89823ce70a Mon Sep 17 00:00:00 2001 From: cfig Date: Sat, 11 Jun 2022 22:26:04 +0800 Subject: [PATCH] additional boot signature support and others Issue #90: support magisk-modified uncompressed ramdisk --- .gitignore | 1 + .../testkey_atx_pik.pem | 0 .../testkey_atx_prk.pem | 0 .../testkey_atx_psk.pem | 0 .../testkey_rsa2048.pem | 0 .../testkey_rsa2048.pk8 | Bin .../testkey_rsa4096.pem | 0 .../testkey_rsa4096.pk8 | Bin .../testkey_rsa8192.pem | 0 .../testkey_rsa8192.pk8 | Bin bbootimg/src/main/kotlin/avb/AVBInfo.kt | 26 ++--- bbootimg/src/main/kotlin/avb/Avb.kt | 81 ++++++++++----- .../src/main/kotlin/avb/alg/Algorithms.kt | 12 +-- .../avb/desc/ChainPartitionDescriptor.kt | 6 +- .../kotlin/avb/desc/HashTreeDescriptor.kt | 4 +- bbootimg/src/main/kotlin/bootimg/Common.kt | 13 +++ bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt | 24 +++-- .../main/kotlin/bootimg/v2/BootV2Dialects.kt | 4 +- bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt | 84 +++++++++++----- .../src/main/kotlin/bootimg/v3/VendorBoot.kt | 16 ++- bbootimg/src/main/kotlin/ota/Payload.kt | 5 +- .../src/main/kotlin/packable/IPackable.kt | 6 +- .../src/main/kotlin/packable/VBMetaParser.kt | 8 +- bbootimg/src/main/kotlin/utils/Dtbo.kt | 4 +- bbootimg/src/main/resources/known_keys.json | 64 ++++++++++++ .../main/kotlin/cfig/helper/CryptoHelper.kt | 28 ++---- .../src/main/kotlin/cfig/helper/Dumpling.kt | 93 ++++++++++++++++++ helper/src/main/kotlin/cfig/helper/Helper.kt | 69 ------------- .../src/main/kotlin/cfig/helper/ZipHelper.kt | 5 + .../kotlin/cfig/helper/CryptoHelperTest.kt | 3 +- .../test/kotlin/cfig/helper/ZipHelperTest.kt | 4 +- 31 files changed, 377 insertions(+), 183 deletions(-) rename aosp/avb/{avb_test_data => data}/testkey_atx_pik.pem (100%) rename aosp/avb/{avb_test_data => data}/testkey_atx_prk.pem (100%) rename aosp/avb/{avb_test_data => data}/testkey_atx_psk.pem (100%) rename aosp/avb/{avb_test_data => data}/testkey_rsa2048.pem (100%) rename aosp/avb/{avb_test_data => data}/testkey_rsa2048.pk8 (100%) rename aosp/avb/{avb_test_data => data}/testkey_rsa4096.pem (100%) rename aosp/avb/{avb_test_data => data}/testkey_rsa4096.pk8 (100%) rename aosp/avb/{avb_test_data => data}/testkey_rsa8192.pem (100%) rename aosp/avb/{avb_test_data => data}/testkey_rsa8192.pk8 (100%) create mode 100644 bbootimg/src/main/resources/known_keys.json create mode 100644 helper/src/main/kotlin/cfig/helper/Dumpling.kt diff --git a/.gitignore b/.gitignore index bfb6357..7e40555 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .gradle build/ local.properties +__pycache__ diff --git a/aosp/avb/avb_test_data/testkey_atx_pik.pem b/aosp/avb/data/testkey_atx_pik.pem similarity index 100% rename from aosp/avb/avb_test_data/testkey_atx_pik.pem rename to aosp/avb/data/testkey_atx_pik.pem diff --git a/aosp/avb/avb_test_data/testkey_atx_prk.pem b/aosp/avb/data/testkey_atx_prk.pem similarity index 100% rename from aosp/avb/avb_test_data/testkey_atx_prk.pem rename to aosp/avb/data/testkey_atx_prk.pem diff --git a/aosp/avb/avb_test_data/testkey_atx_psk.pem b/aosp/avb/data/testkey_atx_psk.pem similarity index 100% rename from aosp/avb/avb_test_data/testkey_atx_psk.pem rename to aosp/avb/data/testkey_atx_psk.pem diff --git a/aosp/avb/avb_test_data/testkey_rsa2048.pem b/aosp/avb/data/testkey_rsa2048.pem similarity index 100% rename from aosp/avb/avb_test_data/testkey_rsa2048.pem rename to aosp/avb/data/testkey_rsa2048.pem diff --git a/aosp/avb/avb_test_data/testkey_rsa2048.pk8 b/aosp/avb/data/testkey_rsa2048.pk8 similarity index 100% rename from aosp/avb/avb_test_data/testkey_rsa2048.pk8 rename to aosp/avb/data/testkey_rsa2048.pk8 diff --git a/aosp/avb/avb_test_data/testkey_rsa4096.pem b/aosp/avb/data/testkey_rsa4096.pem similarity index 100% rename from aosp/avb/avb_test_data/testkey_rsa4096.pem rename to aosp/avb/data/testkey_rsa4096.pem diff --git a/aosp/avb/avb_test_data/testkey_rsa4096.pk8 b/aosp/avb/data/testkey_rsa4096.pk8 similarity index 100% rename from aosp/avb/avb_test_data/testkey_rsa4096.pk8 rename to aosp/avb/data/testkey_rsa4096.pk8 diff --git a/aosp/avb/avb_test_data/testkey_rsa8192.pem b/aosp/avb/data/testkey_rsa8192.pem similarity index 100% rename from aosp/avb/avb_test_data/testkey_rsa8192.pem rename to aosp/avb/data/testkey_rsa8192.pem diff --git a/aosp/avb/avb_test_data/testkey_rsa8192.pk8 b/aosp/avb/data/testkey_rsa8192.pk8 similarity index 100% rename from aosp/avb/avb_test_data/testkey_rsa8192.pk8 rename to aosp/avb/data/testkey_rsa8192.pk8 diff --git a/bbootimg/src/main/kotlin/avb/AVBInfo.kt b/bbootimg/src/main/kotlin/avb/AVBInfo.kt index 13b5d5c..8adf031 100644 --- a/bbootimg/src/main/kotlin/avb/AVBInfo.kt +++ b/bbootimg/src/main/kotlin/avb/AVBInfo.kt @@ -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>() @@ -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 } } diff --git a/bbootimg/src/main/kotlin/avb/Avb.kt b/bbootimg/src/main/kotlin/avb/Avb.kt index d3b9b14..dad1de3 100644 --- a/bbootimg/src/main/kotlin/avb/Avb.kt +++ b/bbootimg/src/main/kotlin/avb/Avb.kt @@ -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 { + fun verify(ai: AVBInfo, dp: Dumpling<*>, parent: String = ""): Array { val ret: Array = 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>() {}) + 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')" + } + } } } diff --git a/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt b/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt index f4159cb..9663f1d 100644 --- a/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt +++ b/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt @@ -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 diff --git a/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt index 27d44c2..6733280 100644 --- a/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt @@ -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 = 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" diff --git a/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt index e5b07fd..ebdb353 100644 --- a/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt @@ -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)) { diff --git a/bbootimg/src/main/kotlin/bootimg/Common.kt b/bbootimg/src/main/kotlin/bootimg/Common.kt index 7e72b0f..879f11a 100644 --- a/bbootimg/src/main/kotlin/bootimg/Common.kt +++ b/bbootimg/src/main/kotlin/bootimg/Common.kt @@ -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") } diff --git a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt index 34cd980..bd230c4 100644 --- a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt +++ b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt @@ -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() diff --git a/bbootimg/src/main/kotlin/bootimg/v2/BootV2Dialects.kt b/bbootimg/src/main/kotlin/bootimg/v2/BootV2Dialects.kt index 328d5a1..1c72cba 100644 --- a/bbootimg/src/main/kotlin/bootimg/v2/BootV2Dialects.kt +++ b/bbootimg/src/main/kotlin/bootimg/v2/BootV2Dialects.kt @@ -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") diff --git a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt index 416762f..5cd7575 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt @@ -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 diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt index 0847824..ec11400 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt @@ -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 { diff --git a/bbootimg/src/main/kotlin/ota/Payload.kt b/bbootimg/src/main/kotlin/ota/Payload.kt index f3c07e7..404ae3a 100644 --- a/bbootimg/src/main/kotlin/ota/Payload.kt +++ b/bbootimg/src/main/kotlin/ota/Payload.kt @@ -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" diff --git a/bbootimg/src/main/kotlin/packable/IPackable.kt b/bbootimg/src/main/kotlin/packable/IPackable.kt index fa76b21..82dd240 100644 --- a/bbootimg/src/main/kotlin/packable/IPackable.kt +++ b/bbootimg/src/main/kotlin/packable/IPackable.kt @@ -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() { diff --git a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt index aa4b183..51c69e4 100644 --- a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt +++ b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt @@ -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) { diff --git a/bbootimg/src/main/kotlin/utils/Dtbo.kt b/bbootimg/src/main/kotlin/utils/Dtbo.kt index e6cc371..024cdcf 100644 --- a/bbootimg/src/main/kotlin/utils/Dtbo.kt +++ b/bbootimg/src/main/kotlin/utils/Dtbo.kt @@ -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") } diff --git a/bbootimg/src/main/resources/known_keys.json b/bbootimg/src/main/resources/known_keys.json new file mode 100644 index 0000000..69041d4 --- /dev/null +++ b/bbootimg/src/main/resources/known_keys.json @@ -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" +} +] diff --git a/helper/src/main/kotlin/cfig/helper/CryptoHelper.kt b/helper/src/main/kotlin/cfig/helper/CryptoHelper.kt index 707f565..3233634 100644 --- a/helper/src/main/kotlin/cfig/helper/CryptoHelper.kt +++ b/helper/src/main/kotlin/cfig/helper/CryptoHelper.kt @@ -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>, algorithm: String): ByteArray { + fun hash(ds: Dumpling<*>, coordinates: List>, 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) + } } } diff --git a/helper/src/main/kotlin/cfig/helper/Dumpling.kt b/helper/src/main/kotlin/cfig/helper/Dumpling.kt new file mode 100644 index 0000000..e7cefbe --- /dev/null +++ b/helper/src/main/kotlin/cfig/helper/Dumpling.kt @@ -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(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): 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") + } + } + } +} diff --git a/helper/src/main/kotlin/cfig/helper/Helper.kt b/helper/src/main/kotlin/cfig/helper/Helper.kt index f066cbc..272fac7 100644 --- a/helper/src/main/kotlin/cfig/helper/Helper.kt +++ b/helper/src/main/kotlin/cfig/helper/Helper.kt @@ -38,75 +38,6 @@ class Helper { var dumpFile: String ) - class DataSrc(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): 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")) diff --git a/helper/src/main/kotlin/cfig/helper/ZipHelper.kt b/helper/src/main/kotlin/cfig/helper/ZipHelper.kt index f82fdf5..c923b1c 100644 --- a/helper/src/main/kotlin/cfig/helper/ZipHelper.kt +++ b/helper/src/main/kotlin/cfig/helper/ZipHelper.kt @@ -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 -> diff --git a/helper/src/test/kotlin/cfig/helper/CryptoHelperTest.kt b/helper/src/test/kotlin/cfig/helper/CryptoHelperTest.kt index 7e6760f..458b115 100644 --- a/helper/src/test/kotlin/cfig/helper/CryptoHelperTest.kt +++ b/helper/src/test/kotlin/cfig/helper/CryptoHelperTest.kt @@ -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)) } } diff --git a/helper/src/test/kotlin/cfig/helper/ZipHelperTest.kt b/helper/src/test/kotlin/cfig/helper/ZipHelperTest.kt index 3bbfcb7..20f18ef 100644 --- a/helper/src/test/kotlin/cfig/helper/ZipHelperTest.kt +++ b/helper/src/test/kotlin/cfig/helper/ZipHelperTest.kt @@ -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)))) } }