Issue #55: fully support boot V4

v3 -> v4 changelog:
 - boot signature for GKI
pull/66/head
cfig 4 years ago
parent b274b358e5
commit b8dd19be1e
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -14,15 +14,198 @@
package avb
import avb.alg.Algorithms
import avb.blob.AuthBlob
import avb.blob.AuxBlob
import avb.blob.Footer
import avb.blob.Header
import avb.desc.*
import cfig.Avb
import cfig.helper.Helper
import cfig.helper.Helper.Companion.paddingWith
import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.codec.binary.Hex
import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileInputStream
/*
a wonderfaul base64 encoder/decoder: https://cryptii.com/base64-to-hex
*/
class AVBInfo(var header: Header? = null,
var authBlob: AuthBlob? = null,
var auxBlob: AuxBlob? = null,
var footer: Footer? = null)
class AVBInfo(
var header: Header? = null,
var authBlob: AuthBlob? = null,
var auxBlob: AuxBlob? = null,
var footer: Footer? = null,
) {
fun encode(): ByteArray {
val alg = Algorithms.get(header!!.algorithm_type)!!
//3 - whole aux blob
val newAuxBlob = auxBlob?.encode(alg) ?: byteArrayOf()
//1 - whole header blob
val headerBlob = this.header!!.apply {
auxiliary_data_block_size = newAuxBlob.size.toLong()
authentication_data_block_size = Helper.round_to_multiple(
(alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64
)
descriptors_offset = 0
descriptors_size = auxBlob?.descriptorSize?.toLong() ?: 0
hash_offset = 0
hash_size = alg.hash_num_bytes.toLong()
signature_offset = alg.hash_num_bytes.toLong()
signature_size = alg.signature_num_bytes.toLong()
public_key_offset = descriptors_size
public_key_size = AuxBlob.encodePubKey(alg).size.toLong()
public_key_metadata_size = auxBlob!!.pubkeyMeta?.pkmd?.size?.toLong() ?: 0L
public_key_metadata_offset = public_key_offset + public_key_size
log.info("pkmd size: $public_key_metadata_size, pkmd offset : $public_key_metadata_offset")
}.encode()
//2 - auth blob
val authBlob = AuthBlob.createBlob(headerBlob, newAuxBlob, alg.name)
val ret = Helper.join(headerBlob, authBlob, newAuxBlob)
//Helper.dumpToFile("_debug_vbmeta_", ret)
return ret
}
fun encodePadded(): ByteArray {
return encode().paddingWith(Avb.BLOCK_SIZE.toUInt())
}
fun dumpDefault(imageFile: String): AVBInfo {
val jsonFile = Avb.getJsonFileName(imageFile)
mapper.writerWithDefaultPrettyPrinter().writeValue(File(jsonFile), this)
log.info("VBMeta: $imageFile -> $jsonFile")
return this
}
companion object {
private val log = LoggerFactory.getLogger(AVBInfo::class.java)
private val mapper = ObjectMapper()
fun parseFrom(imageFile: String): AVBInfo {
log.info("parseFrom($imageFile) ...")
var footer: Footer? = null
var vbMetaOffset: Long = 0
// footer
FileInputStream(imageFile).use { fis ->
fis.skip(File(imageFile).length() - Footer.SIZE)
try {
footer = Footer(fis)
vbMetaOffset = footer!!.vbMetaOffset
log.info("$imageFile: $footer")
} catch (e: IllegalArgumentException) {
log.info("image $imageFile has no AVB Footer")
}
}
// header
val rawHeaderBlob = ByteArray(Header.SIZE).apply {
FileInputStream(imageFile).use { fis ->
fis.skip(vbMetaOffset)
fis.read(this)
}
}
val vbMetaHeader = Header(ByteArrayInputStream(rawHeaderBlob))
log.debug(vbMetaHeader.toString())
log.debug(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(vbMetaHeader))
val authBlockOffset = vbMetaOffset + Header.SIZE
val auxBlockOffset = authBlockOffset + vbMetaHeader.authentication_data_block_size
val ai = AVBInfo(vbMetaHeader, null, AuxBlob(), footer)
// Auth blob
if (vbMetaHeader.authentication_data_block_size > 0) {
FileInputStream(imageFile).use { fis ->
fis.skip(vbMetaOffset)
fis.skip(Header.SIZE.toLong())
fis.skip(vbMetaHeader.hash_offset)
val ba = ByteArray(vbMetaHeader.hash_size.toInt())
fis.read(ba)
log.debug("Parsed Auth Hash (Header & Aux Blob): " + Hex.encodeHexString(ba))
val bb = ByteArray(vbMetaHeader.signature_size.toInt())
fis.read(bb)
log.debug("Parsed Auth Signature (of hash): " + Hex.encodeHexString(bb))
ai.authBlob = AuthBlob()
ai.authBlob!!.offset = authBlockOffset
ai.authBlob!!.size = vbMetaHeader.authentication_data_block_size
ai.authBlob!!.hash = Hex.encodeHexString(ba)
ai.authBlob!!.signature = Hex.encodeHexString(bb)
}
}
// aux
val rawAuxBlob = ByteArray(vbMetaHeader.auxiliary_data_block_size.toInt()).apply {
FileInputStream(imageFile).use { fis ->
fis.skip(auxBlockOffset)
fis.read(this)
}
}
// aux - desc
var descriptors: List<Any>
if (vbMetaHeader.descriptors_size > 0) {
ByteArrayInputStream(rawAuxBlob).use { bis ->
bis.skip(vbMetaHeader.descriptors_offset)
descriptors = UnknownDescriptor.parseDescriptors2(bis, vbMetaHeader.descriptors_size)
}
descriptors.forEach {
log.debug(it.toString())
when (it) {
is PropertyDescriptor -> {
ai.auxBlob!!.propertyDescriptors.add(it)
}
is HashDescriptor -> {
ai.auxBlob!!.hashDescriptors.add(it)
}
is KernelCmdlineDescriptor -> {
ai.auxBlob!!.kernelCmdlineDescriptors.add(it)
}
is HashTreeDescriptor -> {
ai.auxBlob!!.hashTreeDescriptors.add(it)
}
is ChainPartitionDescriptor -> {
ai.auxBlob!!.chainPartitionDescriptors.add(it)
}
is UnknownDescriptor -> {
ai.auxBlob!!.unknownDescriptors.add(it)
}
else -> {
throw IllegalArgumentException("invalid descriptor: $it")
}
}
}
}
// aux - pubkey
if (vbMetaHeader.public_key_size > 0) {
ai.auxBlob!!.pubkey = AuxBlob.PubKeyInfo()
ai.auxBlob!!.pubkey!!.offset = vbMetaHeader.public_key_offset
ai.auxBlob!!.pubkey!!.size = vbMetaHeader.public_key_size
ByteArrayInputStream(rawAuxBlob).use { bis ->
bis.skip(vbMetaHeader.public_key_offset)
ai.auxBlob!!.pubkey!!.pubkey = ByteArray(vbMetaHeader.public_key_size.toInt())
bis.read(ai.auxBlob!!.pubkey!!.pubkey)
log.debug("Parsed Pub Key: " + Hex.encodeHexString(ai.auxBlob!!.pubkey!!.pubkey))
}
}
// aux - pkmd
if (vbMetaHeader.public_key_metadata_size > 0) {
ai.auxBlob!!.pubkeyMeta = AuxBlob.PubKeyMetadataInfo()
ai.auxBlob!!.pubkeyMeta!!.offset = vbMetaHeader.public_key_metadata_offset
ai.auxBlob!!.pubkeyMeta!!.size = vbMetaHeader.public_key_metadata_size
ByteArrayInputStream(rawAuxBlob).use { bis ->
bis.skip(vbMetaHeader.public_key_metadata_offset)
ai.auxBlob!!.pubkeyMeta!!.pkmd = ByteArray(vbMetaHeader.public_key_metadata_size.toInt())
bis.read(ai.auxBlob!!.pubkeyMeta!!.pkmd)
log.debug("Parsed Pub Key Metadata: " + Helper.toHexString(ai.auxBlob!!.pubkeyMeta!!.pkmd))
}
}
log.debug("vbmeta info of [$imageFile] has been analyzed")
return ai
}
}
}

@ -25,13 +25,11 @@ import cfig.helper.Helper
import cfig.helper.Helper.Companion.paddingWith
import cfig.helper.KeyHelper
import cfig.helper.KeyHelper2
import cfig.io.Struct3
import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.codec.binary.Hex
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
@ -42,12 +40,10 @@ import java.nio.file.StandardOpenOption
class Avb {
private val MAX_VBMETA_SIZE = 64 * 1024
private val MAX_FOOTER_SIZE = 4096
private val BLOCK_SIZE = 4096
private val DEBUG = false
//migrated from: avbtool::Avb::addHashFooter
fun addHashFooter(
image_file: String,
image_file: String, //file to be hashed and signed
partition_size: Long, //aligned by Avb::BLOCK_SIZE
partition_name: String,
newAvbInfo: AVBInfo
@ -77,18 +73,13 @@ class Avb {
this.auxBlob!!.hashDescriptors.add(hd)
}
val vbmetaBlob = packVbMeta(newAvbInfo)
log.debug("vbmeta_blob: " + Helper.toHexString(vbmetaBlob))
if (DEBUG) {
Helper.dumpToFile("hashDescriptor.vbmeta.blob", vbmetaBlob)
}
// image + padding
val imgPaddingNeeded = Helper.round_to_multiple(newImageSize, BLOCK_SIZE) - newImageSize
// + vbmeta + padding
val vbmetaBlob = newAvbInfo.encode()
val vbmetaOffset = newImageSize + imgPaddingNeeded
val vbmetaBlobWithPadding = vbmetaBlob.paddingWith(BLOCK_SIZE.toUInt())
val vbmetaBlobWithPadding = newAvbInfo.encodePadded()
// + DONT_CARE chunk
val vbmetaEndOffset = vbmetaOffset + vbmetaBlobWithPadding.size
@ -172,138 +163,6 @@ class Avb {
}
}
fun parseVbMeta(image_file: String, dumpFile: Boolean = true): AVBInfo {
log.info("parseVbMeta($image_file) ...")
val jsonFile = getJsonFileName(image_file)
var footer: Footer? = null
var vbMetaOffset: Long = 0
// footer
FileInputStream(image_file).use { fis ->
fis.skip(File(image_file).length() - Footer.SIZE)
try {
footer = Footer(fis)
vbMetaOffset = footer!!.vbMetaOffset
log.info("$image_file: $footer")
} catch (e: IllegalArgumentException) {
log.info("image $image_file has no AVB Footer")
}
}
// header
val rawHeaderBlob = ByteArray(Header.SIZE).apply {
FileInputStream(image_file).use { fis ->
fis.skip(vbMetaOffset)
fis.read(this)
}
}
val vbMetaHeader = Header(ByteArrayInputStream(rawHeaderBlob))
log.debug(vbMetaHeader.toString())
log.debug(ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(vbMetaHeader))
val authBlockOffset = vbMetaOffset + Header.SIZE
val auxBlockOffset = authBlockOffset + vbMetaHeader.authentication_data_block_size
val ai = AVBInfo(vbMetaHeader, null, AuxBlob(), footer)
// Auth blob
if (vbMetaHeader.authentication_data_block_size > 0) {
FileInputStream(image_file).use { fis ->
fis.skip(vbMetaOffset)
fis.skip(Header.SIZE.toLong())
fis.skip(vbMetaHeader.hash_offset)
val ba = ByteArray(vbMetaHeader.hash_size.toInt())
fis.read(ba)
log.debug("Parsed Auth Hash (Header & Aux Blob): " + Hex.encodeHexString(ba))
val bb = ByteArray(vbMetaHeader.signature_size.toInt())
fis.read(bb)
log.debug("Parsed Auth Signature (of hash): " + Hex.encodeHexString(bb))
ai.authBlob = AuthBlob()
ai.authBlob!!.offset = authBlockOffset
ai.authBlob!!.size = vbMetaHeader.authentication_data_block_size
ai.authBlob!!.hash = Hex.encodeHexString(ba)
ai.authBlob!!.signature = Hex.encodeHexString(bb)
}
}
// aux
val rawAuxBlob = ByteArray(vbMetaHeader.auxiliary_data_block_size.toInt()).apply {
FileInputStream(image_file).use { fis ->
fis.skip(auxBlockOffset)
fis.read(this)
}
}
// aux - desc
var descriptors: List<Any>
if (vbMetaHeader.descriptors_size > 0) {
ByteArrayInputStream(rawAuxBlob).use { bis ->
bis.skip(vbMetaHeader.descriptors_offset)
descriptors = UnknownDescriptor.parseDescriptors2(bis, vbMetaHeader.descriptors_size)
}
descriptors.forEach {
log.debug(it.toString())
when (it) {
is PropertyDescriptor -> {
ai.auxBlob!!.propertyDescriptors.add(it)
}
is HashDescriptor -> {
ai.auxBlob!!.hashDescriptors.add(it)
}
is KernelCmdlineDescriptor -> {
ai.auxBlob!!.kernelCmdlineDescriptors.add(it)
}
is HashTreeDescriptor -> {
ai.auxBlob!!.hashTreeDescriptors.add(it)
}
is ChainPartitionDescriptor -> {
ai.auxBlob!!.chainPartitionDescriptors.add(it)
}
is UnknownDescriptor -> {
ai.auxBlob!!.unknownDescriptors.add(it)
}
else -> {
throw IllegalArgumentException("invalid descriptor: $it")
}
}
}
}
// aux - pubkey
if (vbMetaHeader.public_key_size > 0) {
ai.auxBlob!!.pubkey = AuxBlob.PubKeyInfo()
ai.auxBlob!!.pubkey!!.offset = vbMetaHeader.public_key_offset
ai.auxBlob!!.pubkey!!.size = vbMetaHeader.public_key_size
ByteArrayInputStream(rawAuxBlob).use { bis ->
bis.skip(vbMetaHeader.public_key_offset)
ai.auxBlob!!.pubkey!!.pubkey = ByteArray(vbMetaHeader.public_key_size.toInt())
bis.read(ai.auxBlob!!.pubkey!!.pubkey)
log.debug("Parsed Pub Key: " + Hex.encodeHexString(ai.auxBlob!!.pubkey!!.pubkey))
}
}
// aux - pkmd
if (vbMetaHeader.public_key_metadata_size > 0) {
ai.auxBlob!!.pubkeyMeta = AuxBlob.PubKeyMetadataInfo()
ai.auxBlob!!.pubkeyMeta!!.offset = vbMetaHeader.public_key_metadata_offset
ai.auxBlob!!.pubkeyMeta!!.size = vbMetaHeader.public_key_metadata_size
ByteArrayInputStream(rawAuxBlob).use { bis ->
bis.skip(vbMetaHeader.public_key_metadata_offset)
ai.auxBlob!!.pubkeyMeta!!.pkmd = ByteArray(vbMetaHeader.public_key_metadata_size.toInt())
bis.read(ai.auxBlob!!.pubkeyMeta!!.pkmd)
log.debug("Parsed Pub Key Metadata: " + Helper.toHexString(ai.auxBlob!!.pubkeyMeta!!.pkmd))
}
}
if (dumpFile) {
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(jsonFile), ai)
log.info("parseVbMeta($image_file) done. Result: $jsonFile")
} else {
log.debug("vbmeta info of [$image_file] has been analyzed, no dummping")
}
return ai
}
fun verify(ai: AVBInfo, image_file: String, parent: String = ""): Array<Any> {
val ret: Array<Any> = arrayOf(true, "")
val localParent = if (parent.isEmpty()) image_file else parent
@ -397,54 +256,9 @@ class Avb {
return ret
}
private fun packVbMeta(info: AVBInfo? = null, image_file: String? = null): ByteArray {
val ai = info ?: ObjectMapper().readValue(File(getJsonFileName(image_file!!)), AVBInfo::class.java)
val alg = Algorithms.get(ai.header!!.algorithm_type)!!
//3 - whole aux blob
val auxBlob = ai.auxBlob?.encode(alg) ?: byteArrayOf()
//1 - whole header blob
val headerBlob = ai.header!!.apply {
auxiliary_data_block_size = auxBlob.size.toLong()
authentication_data_block_size = Helper.round_to_multiple(
(alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64
)
descriptors_offset = 0
descriptors_size = ai.auxBlob?.descriptorSize?.toLong() ?: 0
hash_offset = 0
hash_size = alg.hash_num_bytes.toLong()
signature_offset = alg.hash_num_bytes.toLong()
signature_size = alg.signature_num_bytes.toLong()
public_key_offset = descriptors_size
public_key_size = AuxBlob.encodePubKey(alg).size.toLong()
public_key_metadata_size = ai.auxBlob!!.pubkeyMeta?.pkmd?.size?.toLong() ?: 0L
public_key_metadata_offset = public_key_offset + public_key_size
log.info("pkmd size: $public_key_metadata_size, pkmd offset : $public_key_metadata_offset")
}.encode()
//2 - auth blob
val authBlob = AuthBlob.createBlob(headerBlob, auxBlob, alg.name)
return Helper.join(headerBlob, authBlob, auxBlob)
}
fun packVbMetaWithPadding(image_file: String? = null, info: AVBInfo? = null) {
val rawBlob = packVbMeta(info, image_file)
val paddingSize = Helper.round_to_multiple(rawBlob.size.toLong(), BLOCK_SIZE) - rawBlob.size
val paddedBlob = Helper.join(rawBlob, Struct3("${paddingSize}x").pack(null))
log.info("raw vbmeta size ${rawBlob.size}, padding size $paddingSize, total blob size ${paddedBlob.size}")
log.info("Writing padded vbmeta to file: $image_file.signed")
Files.write(Paths.get("$image_file.signed"), paddedBlob, StandardOpenOption.CREATE)
}
companion object {
private val log = LoggerFactory.getLogger(Avb::class.java)
const val BLOCK_SIZE = 4096
const val AVB_VERSION_MAJOR = 1
const val AVB_VERSION_MINOR = 1
const val AVB_VERSION_SUB = 0
@ -483,7 +297,8 @@ class Avb {
ObjectMapper().readValue(File(getJsonFileName(fileName)), AVBInfo::class.java).let {
it.auxBlob!!.hashDescriptors.get(0).partition_name
}
val newHashDesc = Avb().parseVbMeta("$fileName.signed", dumpFile = false)
//read hashDescriptor from image
val newHashDesc = AVBInfo.parseFrom("$fileName.signed")
assert(newHashDesc.auxBlob!!.hashDescriptors.size == 1)
var seq = -1 //means not found
//main vbmeta
@ -501,9 +316,11 @@ class Avb {
if (-1 == seq) {
log.warn("main vbmeta doesn't have $partitionName hashDescriptor, won't update vbmeta.img")
} else {
//add hashDescriptor back to main vbmeta
val hd = newHashDesc.auxBlob!!.hashDescriptors.get(0).apply { this.sequence = seq }
this.auxBlob!!.hashDescriptors.add(hd)
Avb().packVbMetaWithPadding("vbmeta.img", this)
log.info("Writing padded vbmeta to file: vbmeta.img.signed")
Files.write(Paths.get("vbmeta.img.signed"), encodePadded(), StandardOpenOption.CREATE)
log.info("Updating vbmeta.img side by side (partition=$partitionName, seq=$seq) done")
}
}

@ -14,6 +14,7 @@
package avb.desc
import avb.AVBInfo
import cfig.Avb
import cfig.helper.Helper
import cfig.io.Struct3
@ -84,7 +85,7 @@ class ChainPartitionDescriptor(
val ret: Array<Any> = arrayOf(false, "file not found")
for (item in image_files) {
if (File(item).exists()) {
val subAi = Avb().parseVbMeta(item, false)
val subAi = AVBInfo.parseFrom(item)
if (pubkey.contentEquals(subAi.auxBlob!!.pubkey!!.pubkey)) {
log.info("VERIFY($parent): public key matches, PASS")
return Avb().verify(subAi, item, parent)

@ -33,7 +33,7 @@ class Signer {
fun signAVB(output: String, imageSize: Long, avbtool: String) {
log.info("Adding hash_footer with verified-boot 2.0 style")
val ai = ObjectMapper().readValue(File(getJsonFileName(output)), AVBInfo::class.java)
val alg = Algorithms.get(ai.header!!.algorithm_type.toInt())
val alg = Algorithms.get(ai.header!!.algorithm_type)
val bootDesc = ai.auxBlob!!.hashDescriptors[0]
val newAvbInfo = ObjectMapper().readValue(File(getJsonFileName(output)), AVBInfo::class.java)

@ -14,6 +14,7 @@
package cfig.bootimg.v2
import avb.AVBInfo
import cfig.Avb
import cfig.EnvironmentVerifier
import cfig.bootimg.Common
@ -38,7 +39,7 @@ data class BootV2(
var ramdisk: CommArgs = CommArgs(),
var secondBootloader: CommArgs? = null,
var recoveryDtbo: CommArgsLong? = null,
var dtb: CommArgsLong? = null
var dtb: CommArgsLong? = null,
) {
data class MiscInfo(
var output: String = "",
@ -54,21 +55,21 @@ data class BootV2(
var osPatchLevel: String? = null,
var hash: ByteArray? = byteArrayOf(),
var verify: String = "",
var imageSize: Long = 0
var imageSize: Long = 0,
)
data class CommArgs(
var file: String? = null,
var position: Long = 0,
var size: Int = 0,
var loadOffset: Long = 0
var loadOffset: Long = 0,
)
data class CommArgsLong(
var file: String? = null,
var position: Long = 0,
var size: Int = 0,
var loadOffset: Long = 0
var loadOffset: Long = 0,
)
companion object {
@ -223,7 +224,7 @@ data class BootV2(
fun extractVBMeta(): BootV2 {
if (this.info.verify.startsWith("VB2.0")) {
Avb().parseVbMeta(info.output)
AVBInfo.parseFrom(info.output).dumpDefault(info.output)
if (File("vbmeta.img").exists()) {
log.warn("Found vbmeta.img, parsing ...")
VBMetaParser().unpack("vbmeta.img")

@ -14,6 +14,9 @@
package cfig.bootimg.v3
import avb.AVBInfo
import avb.alg.Algorithms
import avb.blob.AuxBlob
import cfig.Avb
import cfig.EnvironmentVerifier
import cfig.bootimg.Common.Companion.deleleIfExists
@ -37,10 +40,11 @@ data class BootV3(
var info: MiscInfo = MiscInfo(),
var kernel: CommArgs = CommArgs(),
val ramdisk: CommArgs = CommArgs(),
var bootSignature: CommArgs = CommArgs()
var bootSignature: CommArgs = CommArgs(),
) {
companion object {
private val log = LoggerFactory.getLogger(BootV3::class.java)
private val mapper = ObjectMapper()
private val workDir = Helper.prop("workDir")
fun parse(fileName: String): BootV3 {
@ -89,13 +93,13 @@ data class BootV3(
var osVersion: String = "",
var osPatchLevel: String = "",
var imageSize: Long = 0,
var signatureSize: Int = 0
var signatureSize: Int = 0,
)
data class CommArgs(
var file: String = "",
var position: Int = 0,
var size: Int = 0
var size: Int = 0,
)
fun pack(): BootV3 {
@ -129,11 +133,31 @@ data class BootV3(
bf.order(ByteOrder.LITTLE_ENDIAN)
C.writePaddedFile(bf, this.kernel.file, this.info.pageSize)
C.writePaddedFile(bf, this.ramdisk.file, this.info.pageSize)
//write
//write V3 data
FileOutputStream("${this.info.output}.clear", true).use { fos ->
fos.write(bf.array(), 0, bf.position())
}
//write V4 boot sig
if (this.info.headerVersion > 3) {
val bootSigJson = File(Avb.getJsonFileName(this.bootSignature.file))
var bootSigBytes = ByteArray(this.bootSignature.size)
if (bootSigJson.exists()) {
log.warn("V4 BootImage has GKI boot signature")
val readBackBootSig = mapper.readValue(bootSigJson, AVBInfo::class.java)
val alg = Algorithms.get(readBackBootSig.header!!.algorithm_type)!!
//replace new pub key
readBackBootSig.auxBlob!!.pubkey!!.pubkey = AuxBlob.encodePubKey(alg)
//update hash and sig
readBackBootSig.auxBlob!!.hashDescriptors.get(0).update(this.info.output + ".clear")
bootSigBytes = readBackBootSig.encodePadded()
}
//write V4 data
FileOutputStream("${this.info.output}.clear", true).use { fos ->
fos.write(bootSigBytes)
}
}
//google way
this.toCommandLine().addArgument(this.info.output + ".google").let {
log.info(it.toString())
@ -161,14 +185,15 @@ data class BootV3(
osVersion = info.osVersion,
osPatchLevel = info.osPatchLevel,
headerSize = info.headerSize,
cmdline = info.cmdline
cmdline = info.cmdline,
signatureSize = info.signatureSize
)
}
fun extractImages(): BootV3 {
val workDir = Helper.prop("workDir")
//info
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
//kernel
C.dumpKernel(Helper.Slice(info.output, kernel.position, kernel.size, kernel.file))
//ramdisk
@ -183,20 +208,20 @@ data class BootV3(
this.bootSignature.position.toLong(), this.bootSignature.size
)
try {
Avb().parseVbMeta(this.bootSignature.file)
AVBInfo.parseFrom(this.bootSignature.file).dumpDefault(this.bootSignature.file)
} catch (e: IllegalArgumentException) {
log.warn("boot signature is invalid")
}
}
//dump info again
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
return this
}
fun extractVBMeta(): BootV3 {
try {
Avb().parseVbMeta(info.output)
AVBInfo.parseFrom(info.output).dumpDefault(info.output)
if (File("vbmeta.img").exists()) {
log.warn("Found vbmeta.img, parsing ...")
VBMetaParser().unpack("vbmeta.img")
@ -290,6 +315,12 @@ data class BootV3(
ret.addArgument(" --os_patch_level")
ret.addArgument(info.osPatchLevel)
}
if (this.bootSignature.size > 0 && File(Avb.getJsonFileName(this.bootSignature.file)).exists()) {
val origSig = mapper.readValue(File(Avb.getJsonFileName(this.bootSignature.file)), AVBInfo::class.java)
val alg = Algorithms.get(origSig.header!!.algorithm_type)!!
ret.addArgument("--gki_signing_algorithm").addArgument(alg.name)
ret.addArgument("--gki_signing_key").addArgument(alg.defaultKey)
}
ret.addArgument(" --id ")
ret.addArgument(" --output ")
//ret.addArgument("boot.img" + ".google")

@ -14,6 +14,7 @@
package cfig.bootimg.v3
import avb.AVBInfo
import cfig.Avb
import cfig.EnvironmentVerifier
import cfig.bootimg.Common.Companion.deleleIfExists
@ -37,13 +38,13 @@ data class VendorBoot(
var ramdisk: CommArgs = CommArgs(),
var dtb: CommArgs = CommArgs(),
var ramdisk_table: Vrt = Vrt(),
var bootconfig: CommArgs = CommArgs()
var bootconfig: CommArgs = CommArgs(),
) {
data class CommArgs(
var file: String = "",
var position: Long = 0,
var size: Int = 0,
var loadAddr: Long = 0
var loadAddr: Long = 0,
)
data class MiscInfo(
@ -56,7 +57,7 @@ data class VendorBoot(
var cmdline: String = "",
var tagsLoadAddr: Long = 0,
var kernelLoadAddr: Long = 0,
var imageSize: Long = 0
var imageSize: Long = 0,
)
enum class VrtType {
@ -82,7 +83,7 @@ data class VendorBoot(
var size: Int = 0,
var eachEntrySize: Int = 0,
var position: Long = 0,
var ramdidks: MutableList<VrtEntry> = mutableListOf()
var ramdidks: MutableList<VrtEntry> = mutableListOf(),
) {
fun update(): Vrt {
var totalSz = 0
@ -113,11 +114,10 @@ data class VendorBoot(
var name: String = "", //32s
var boardId: ByteArray = byteArrayOf(), //16I (aka. 64 bytes)
var boardIdStr: String = "",
var file: String = ""
var file: String = "",
) {
companion object {
private val log = LoggerFactory.getLogger(VrtEntry::class.java)
const val VENDOR_RAMDISK_NAME_SIZE = 32
private const val VENDOR_RAMDISK_NAME_SIZE = 32
const val VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
//const val FORMAT_STRING = "3I${VENDOR_RAMDISK_NAME_SIZE}s${VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I"
@ -156,7 +156,6 @@ data class VendorBoot(
companion object {
private val log = LoggerFactory.getLogger(VendorBoot::class.java)
private val workDir = Helper.prop("workDir")
private val VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE = 108
fun parse(fileName: String): VendorBoot {
val ret = VendorBoot()
FileInputStream(fileName).use { fis ->
@ -342,7 +341,7 @@ data class VendorBoot(
fun extractVBMeta(): VendorBoot {
try {
Avb().parseVbMeta(info.output)
AVBInfo.parseFrom(info.output).dumpDefault(info.output)
} catch (e: Exception) {
log.error("extraceVBMeta(): $e")
}

@ -14,6 +14,7 @@
package cfig.packable
import avb.AVBInfo
import cfig.Avb
import cfig.helper.Helper
import cfig.helper.Helper.Companion.check_call
@ -62,7 +63,7 @@ interface IPackable {
// invoked solely by reflection
fun `@verify`(fileName: String) {
val ai = Avb().parseVbMeta(fileName, true)
val ai = AVBInfo.parseFrom(fileName).dumpDefault(fileName)
Avb().verify(ai, fileName)
}

@ -14,9 +14,15 @@
package cfig.packable
import avb.AVBInfo
import cfig.Avb
import cfig.helper.Helper
import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.LoggerFactory
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardOpenOption
class VBMetaParser: IPackable {
override val loopNo: Int
@ -32,11 +38,13 @@ class VBMetaParser: IPackable {
override fun unpack(fileName: String) {
cleanUp()
Avb().parseVbMeta(fileName)
AVBInfo.parseFrom(fileName).dumpDefault(fileName)
}
override fun pack(fileName: String) {
Avb().packVbMetaWithPadding(fileName)
val blob = ObjectMapper().readValue(File(Avb.getJsonFileName(fileName)), AVBInfo::class.java).encodePadded()
log.info("Writing padded vbmeta to file: $fileName.signed")
Files.write(Paths.get("$fileName.signed"), blob, StandardOpenOption.CREATE)
}
override fun flash(fileName: String, deviceName: String) {
@ -51,4 +59,6 @@ class VBMetaParser: IPackable {
override fun pull(fileName: String, deviceName: String) {
super.pull(fileName, deviceName)
}
private val log = LoggerFactory.getLogger(VBMetaParser::class.java)
}

@ -46,7 +46,6 @@ import java.util.regex.Pattern
import kotlin.random.Random
class Struct3 {
private val log = LoggerFactory.getLogger(Struct3::class.java)
private val formatString: String
private var byteOrder = ByteOrder.LITTLE_ENDIAN
private val formats = ArrayList<Array<Any?>>()
@ -135,10 +134,10 @@ class Struct3 {
return ret
}
@Throws(IllegalArgumentException::class)
fun pack(vararg args: Any?): ByteArray {
if (args.size != this.formats.size) {
throw IllegalArgumentException("argument size " + args.size +
" doesn't match format size " + this.formats.size)
throw IllegalArgumentException("argument size " + args.size + " doesn't match format size " + this.formats.size)
}
val bf = ByteBuffer.allocate(this.calcSize())
bf.order(this.byteOrder)
@ -157,8 +156,7 @@ class Struct3 {
null -> bf.appendPadding(0, multiple)
is Byte -> bf.appendPadding(arg, multiple)
is Int -> bf.appendPadding(arg.toByte(), multiple)
else -> throw IllegalArgumentException("Index[" + i + "] Unsupported arg ["
+ arg + "] with type [" + formats[i][0] + "]")
else -> throw IllegalArgumentException("Index[" + i + "] Unsupported arg [" + arg + "] with type [" + formats[i][0] + "]")
}
continue
}
@ -288,7 +286,7 @@ class Struct3 {
return bf.array()
}
@Throws(IOException::class)
@Throws(IOException::class, IllegalArgumentException::class)
fun unpack(iS: InputStream): List<*> {
val ret = ArrayList<Any>()
for (format in this.formats) {
@ -314,6 +312,7 @@ class Struct3 {
companion object {
private val log = LoggerFactory.getLogger(ByteBufferExt::class.java)
@Throws(IllegalArgumentException::class)
fun ByteBuffer.appendPadding(b: Byte, bufSize: Int) {
when {
bufSize == 0 -> {
@ -334,15 +333,17 @@ class Struct3 {
fun ByteBuffer.appendByteArray(inIntArray: IntArray, bufSize: Int) {
val arg2 = mutableListOf<Byte>()
inIntArray.toMutableList().mapTo(arg2, {
if (it in Byte.MIN_VALUE..Byte.MAX_VALUE)
inIntArray.toMutableList().mapTo(arg2) {
if (it in Byte.MIN_VALUE..Byte.MAX_VALUE) {
it.toByte()
else
} else {
throw IllegalArgumentException("$it is not valid Byte")
})
}
}
appendByteArray(arg2.toByteArray(), bufSize)
}
@Throws(IllegalArgumentException::class)
fun ByteBuffer.appendByteArray(inByteArray: ByteArray, bufSize: Int) {
val paddingSize = bufSize - inByteArray.size
if (paddingSize < 0) throw IllegalArgumentException("arg length [${inByteArray.size}] exceeds limit: $bufSize")
@ -355,18 +356,19 @@ class Struct3 {
fun ByteBuffer.appendUByteArray(inIntArray: IntArray, bufSize: Int) {
val arg2 = mutableListOf<UByte>()
inIntArray.toMutableList().mapTo(arg2, {
inIntArray.toMutableList().mapTo(arg2) {
if (it in UByte.MIN_VALUE.toInt()..UByte.MAX_VALUE.toInt())
it.toUByte()
else
else {
throw IllegalArgumentException("$it is not valid Byte")
})
}
}
appendUByteArray(arg2.toUByteArray(), bufSize)
}
fun ByteBuffer.appendUByteArray(inUByteArray: UByteArray, bufSize: Int) {
val bl = mutableListOf<Byte>()
inUByteArray.toMutableList().mapTo(bl, { it.toByte() })
inUByteArray.toMutableList().mapTo(bl) { it.toByte() }
this.appendByteArray(bl.toByteArray(), bufSize)
}
}
@ -426,7 +428,7 @@ class Struct3 {
val data = ByteArray(inSize)
assert(inSize == this.read(data))
val innerData2 = mutableListOf<UByte>()
data.toMutableList().mapTo(innerData2, { it.toUByte() })
data.toMutableList().mapTo(innerData2) { it.toUByte() }
return innerData2.toUByteArray()
}
@ -451,92 +453,78 @@ class Struct3 {
fun ByteArray.toShort(inByteOrder: ByteOrder): Short {
val typeSize = Short.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == this.size) { "Short must have $typeSize bytes" }
var ret: Short
ByteBuffer.allocate(typeSize).let {
return ByteBuffer.allocate(this.size).let {
it.order(inByteOrder)
it.put(this)
it.flip()
ret = it.getShort()
it.getShort()
}
return ret
}
fun ByteArray.toInt(inByteOrder: ByteOrder): Int {
val typeSize = Int.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == this.size) { "Int must have $typeSize bytes" }
var ret: Int
ByteBuffer.allocate(typeSize).let {
return ByteBuffer.allocate(this.size).let {
it.order(inByteOrder)
it.put(this)
it.flip()
ret = it.getInt()
it.getInt()
}
return ret
}
fun ByteArray.toLong(inByteOrder: ByteOrder): Long {
val typeSize = Long.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == this.size) { "Long must have $typeSize bytes" }
var ret: Long
ByteBuffer.allocate(typeSize).let {
return ByteBuffer.allocate(this.size).let {
it.order(inByteOrder)
it.put(this)
it.flip()
ret = it.getLong()
it.getLong()
}
return ret
}
fun ByteArray.toUShort(inByteOrder: ByteOrder): UShort {
val typeSize = UShort.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == this.size) { "UShort must have $typeSize bytes" }
var ret: UShort
ByteBuffer.allocate(typeSize).let {
return ByteBuffer.allocate(this.size).let {
it.order(inByteOrder)
it.put(this)
it.flip()
ret = it.getShort().toUShort()
it.getShort().toUShort()
}
return ret
}
fun ByteArray.toUInt(inByteOrder: ByteOrder): UInt {
val typeSize = UInt.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == this.size) { "UInt must have $typeSize bytes" }
var ret: UInt
ByteBuffer.allocate(typeSize).let {
return ByteBuffer.allocate(this.size).let {
it.order(inByteOrder)
it.put(this)
it.flip()
ret = it.getInt().toUInt()
it.getInt().toUInt()
}
return ret
}
fun ByteArray.toULong(inByteOrder: ByteOrder): ULong {
val typeSize = ULong.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == this.size) { "ULong must have $typeSize bytes" }
var ret: ULong
ByteBuffer.allocate(typeSize).let {
return ByteBuffer.allocate(this.size).let {
it.order(inByteOrder)
it.put(this)
it.flip()
ret = it.getLong().toULong()
it.getLong().toULong()
}
return ret
}
//similar to this.toString(StandardCharsets.UTF_8).replace("${Character.MIN_VALUE}", "")
// not Deprecated for now, "1.3.41 experimental api: ByteArray.decodeToString()") is a little different
fun ByteArray.toCString(): String {
val str = this.toString(StandardCharsets.UTF_8)
val nullPos = str.indexOf(Character.MIN_VALUE)
return if (nullPos >= 0) {
str.substring(0, nullPos)
} else {
str
return this.toString(StandardCharsets.UTF_8).let { str ->
str.indexOf(Character.MIN_VALUE).let { nullPos ->
if (nullPos >= 0) str.substring(0, nullPos) else str
}
}
}
}
}
}//end-of-Companion
}//end-of-ByteArrayExt
}

Loading…
Cancel
Save