using newly added kotlin.(UInt|ULong|UByte) since 1.3

add 'Reboot' function tests
add "Struct3" to replace "Struct" with new kotlin types
pull/31/head
cfig 6 years ago
parent fe14b49178
commit a7be076e77
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -1,6 +1,6 @@
buildscript {
ext {
kotlinVersion = "1.3.20"
kotlinVersion = "1.3.30"
}
repositories {
mavenCentral()

@ -28,6 +28,9 @@ public class Struct {
if (formatString.startsWith(">") || formatString.startsWith("!")) {
this.byteOrder = ByteOrder.BIG_ENDIAN;
log.debug("Parsing BIG_ENDIAN format: " + formatString);
} else if (formatString.startsWith("@") || formatString.startsWith("=")) {
this.byteOrder = ByteOrder.nativeOrder();
log.debug("Parsing native ENDIAN format: " + formatString);
} else {
log.debug("Parsing LITTLE_ENDIAN format: " + formatString);
}
@ -38,7 +41,12 @@ public class Struct {
if (!m.group(1).isEmpty()) {
mul = Integer.decode(m.group(1));
}
//item[0]: Type, item[1]: multiple
// if need to expand format items, explode it
// eg: "4L" will be exploded to "1L 1L 1L 1L"
// eg: "10x" won't be exploded, it's still "10x"
Object item[] = new Object[2];
switch (m.group(2)) {
case "x": {//byte 1
item[0] = PadByte.class;
@ -98,7 +106,7 @@ public class Struct {
}
public Integer calcSize() {
Integer ret = 0;
int ret = 0;
for (Object[] format : formats) {
if (format[0] == Byte.class || format[0] == Character.class || format[0] == PadByte.class) {
ret += (int) format[1];
@ -239,7 +247,7 @@ public class Struct {
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
Class<?> format = (Class<?>) formats.get(i)[0];
Integer size = (int) formats.get(i)[1];
int size = (int) formats.get(i)[1];
log.debug("Index[" + i + "], fmt = " + format + ", arg = " + arg + ", multi = " + size);
//padding

@ -4,7 +4,7 @@ import avb.*
import avb.alg.Algorithms
import avb.desc.*
import avb.AuxBlob
import cfig.io.Struct
import cfig.io.Struct3
import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.codec.binary.Hex
import org.slf4j.LoggerFactory
@ -35,7 +35,7 @@ class Avb {
common_algorithm: String,
inReleaseString: String?) {
log.info("add_hash_footer($image_file) ...")
var original_image_size: Long
var original_image_size: ULong
//required libavb version
if (use_persistent_digest || do_not_use_ab) {
required_libavb_version_minor = 1
@ -72,11 +72,11 @@ class Avb {
FileOutputStream(File(image_file), true).channel.use {
log.info("original image $image_file has AVB footer, " +
"truncate it to original SIZE: ${footer.originalImageSize}")
it.truncate(footer.originalImageSize)
it.truncate(footer.originalImageSize.toLong())
}
} catch (e: IllegalArgumentException) {
log.info("original image $image_file doesn't have AVB footer")
original_image_size = originalFileSize
original_image_size = originalFileSize.toULong()
}
//salt
@ -103,12 +103,12 @@ class Avb {
//HashDescriptor
val hd = HashDescriptor()
hd.image_size = File(image_file).length()
hd.hash_algorithm = hash_algorithm.toByteArray()
hd.image_size = File(image_file).length().toULong()
hd.hash_algorithm = hash_algorithm
hd.partition_name = partition_name
hd.salt = saltByteArray
hd.flags = 0
if (do_not_use_ab) hd.flags = hd.flags or 1
hd.flags = 0U
if (do_not_use_ab) hd.flags = hd.flags or 1u
if (!use_persistent_digest) hd.digest = digest
log.info("encoded hash descriptor:" + Hex.encodeHexString(hd.encode()))
@ -121,15 +121,15 @@ class Avb {
0,
null,
null,
0,
0U,
inReleaseString)
log.debug("vbmeta_blob: " + Helper.toHexString(vbmeta_blob))
Helper.dumpToFile("hashDescriptor.vbmeta.blob", vbmeta_blob)
log.info("Padding image ...")
// image + padding
if (hd.image_size % BLOCK_SIZE != 0L) {
val padding_needed = BLOCK_SIZE - (hd.image_size % BLOCK_SIZE)
if (hd.image_size.toLong() % BLOCK_SIZE != 0L) {
val padding_needed = BLOCK_SIZE - (hd.image_size.toLong() % BLOCK_SIZE)
FileOutputStream(image_file, true).use { fos ->
fos.write(ByteArray(padding_needed.toInt()))
}
@ -142,7 +142,7 @@ class Avb {
log.info("Appending vbmeta ...")
val vbmeta_offset = File(image_file).length()
val padding_needed = Helper.round_to_multiple(vbmeta_blob.size.toLong(), BLOCK_SIZE) - vbmeta_blob.size
val vbmeta_blob_with_padding = Helper.join(vbmeta_blob, Struct("${padding_needed}x").pack(null))
val vbmeta_blob_with_padding = Helper.join(vbmeta_blob, Struct3("${padding_needed}x").pack(null))
FileOutputStream(image_file, true).use { fos ->
fos.write(vbmeta_blob_with_padding)
}
@ -151,18 +151,18 @@ class Avb {
log.info("Appending DONT_CARE chunk ...")
val vbmeta_end_offset = vbmeta_offset + vbmeta_blob_with_padding.size
FileOutputStream(image_file, true).use { fos ->
fos.write(Struct("${partition_size - vbmeta_end_offset - 1 * BLOCK_SIZE}x").pack(null))
fos.write(Struct3("${partition_size - vbmeta_end_offset - 1 * BLOCK_SIZE}x").pack(null))
}
// + AvbFooter + padding
log.info("Appending footer ...")
val footer = Footer()
footer.originalImageSize = original_image_size
footer.vbMetaOffset = vbmeta_offset
footer.vbMetaSize = vbmeta_blob.size.toLong()
footer.vbMetaOffset = vbmeta_offset.toULong()
footer.vbMetaSize = vbmeta_blob.size.toULong()
val footerBob = footer.encode()
val footerBlobWithPadding = Helper.join(
Struct("${BLOCK_SIZE - Footer.SIZE}x").pack(null), footerBob)
Struct3("${BLOCK_SIZE - Footer.SIZE}x").pack(null), footerBob)
log.info("footer:" + Helper.toHexString(footerBob))
log.info(footer.toString())
FileOutputStream(image_file, true).use { fos ->
@ -181,7 +181,7 @@ class Avb {
inFlags: Long,
props: Map<String, String>?,
kernel_cmdlines: List<String>?,
required_libavb_version_minor: Int,
required_libavb_version_minor: UInt,
inReleaseString: String?): ByteArray {
//encoded descriptors
var encodedDesc: ByteArray = byteArrayOf()
@ -207,31 +207,31 @@ class Avb {
//1 - whole header blob
val headerBlob = Header().apply {
bump_required_libavb_version_minor(required_libavb_version_minor)
auxiliary_data_block_size = auxBlob.size.toLong()
auxiliary_data_block_size = auxBlob.size.toULong()
authentication_data_block_size = Helper.round_to_multiple(
(alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64)
(alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64).toULong()
algorithm_type = alg.algorithm_type.toLong()
algorithm_type = alg.algorithm_type.toUInt()
hash_offset = 0
hash_size = alg.hash_num_bytes.toLong()
hash_offset = 0U
hash_size = alg.hash_num_bytes.toULong()
signature_offset = alg.hash_num_bytes.toLong()
signature_size = alg.signature_num_bytes.toLong()
signature_offset = alg.hash_num_bytes.toULong()
signature_size = alg.signature_num_bytes.toULong()
descriptors_offset = 0
descriptors_size = encodedDesc.size.toLong()
descriptors_offset = 0U
descriptors_size = encodedDesc.size.toULong()
public_key_offset = descriptors_size
public_key_size = encodedKey.size.toLong()
public_key_size = encodedKey.size.toULong()
//TODO: support pubkey metadata
public_key_metadata_size = 0
public_key_metadata_size = 0U
public_key_metadata_offset = public_key_offset + public_key_size
rollback_index = inRollbackIndex
flags = inFlags
rollback_index = inRollbackIndex.toULong()
flags = inFlags.toUInt()
if (inReleaseString != null) {
log.info("Using preset release string: $inReleaseString")
this.release_string = inReleaseString
@ -248,7 +248,7 @@ class Avb {
log.info("parsing $image_file ...")
val jsonFile = getJsonFileName(image_file)
var footer: Footer? = null
var vbMetaOffset = 0L
var vbMetaOffset: ULong = 0U
FileInputStream(image_file).use { fis ->
fis.skip(File(image_file).length() - Footer.SIZE)
try {
@ -262,13 +262,13 @@ class Avb {
var vbMetaHeader = Header()
FileInputStream(image_file).use { fis ->
fis.skip(vbMetaOffset)
fis.skip(vbMetaOffset.toLong())
vbMetaHeader = Header(fis)
}
log.info(vbMetaHeader.toString())
log.debug(ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(vbMetaHeader))
val authBlockOffset = vbMetaOffset + Header.SIZE
val authBlockOffset = vbMetaOffset + Header.SIZE.toUInt()
val auxBlockOffset = authBlockOffset + vbMetaHeader.authentication_data_block_size
val descStartOffset = auxBlockOffset + vbMetaHeader.descriptors_offset
@ -276,22 +276,22 @@ class Avb {
ai.footer = footer
ai.auxBlob = AuxBlob()
ai.header = vbMetaHeader
if (vbMetaHeader.public_key_size > 0L) {
if (vbMetaHeader.public_key_size > 0U) {
ai.auxBlob!!.pubkey = AuxBlob.PubKeyInfo()
ai.auxBlob!!.pubkey!!.offset = vbMetaHeader.public_key_offset
ai.auxBlob!!.pubkey!!.size = vbMetaHeader.public_key_size
ai.auxBlob!!.pubkey!!.offset = vbMetaHeader.public_key_offset.toLong()
ai.auxBlob!!.pubkey!!.size = vbMetaHeader.public_key_size.toLong()
}
if (vbMetaHeader.public_key_metadata_size > 0L) {
if (vbMetaHeader.public_key_metadata_size > 0U) {
ai.auxBlob!!.pubkeyMeta = AuxBlob.PubKeyMetadataInfo()
ai.auxBlob!!.pubkeyMeta!!.offset = vbMetaHeader.public_key_metadata_offset
ai.auxBlob!!.pubkeyMeta!!.size = vbMetaHeader.public_key_metadata_size
ai.auxBlob!!.pubkeyMeta!!.offset = vbMetaHeader.public_key_metadata_offset.toLong()
ai.auxBlob!!.pubkeyMeta!!.size = vbMetaHeader.public_key_metadata_size.toLong()
}
var descriptors: List<Any> = mutableListOf()
if (vbMetaHeader.descriptors_size > 0) {
if (vbMetaHeader.descriptors_size > 0U) {
FileInputStream(image_file).use { fis ->
fis.skip(descStartOffset)
descriptors = UnknownDescriptor.parseDescriptors2(fis, vbMetaHeader.descriptors_size)
fis.skip(descStartOffset.toLong())
descriptors = UnknownDescriptor.parseDescriptors2(fis, vbMetaHeader.descriptors_size.toLong())
}
descriptors.forEach {
@ -299,32 +299,32 @@ class Avb {
}
}
if (vbMetaHeader.public_key_size > 0) {
if (vbMetaHeader.public_key_size > 0U) {
FileInputStream(image_file).use { fis ->
fis.skip(auxBlockOffset)
fis.skip(vbMetaHeader.public_key_offset)
fis.skip(auxBlockOffset.toLong())
fis.skip(vbMetaHeader.public_key_offset.toLong())
ai.auxBlob!!.pubkey!!.pubkey = ByteArray(vbMetaHeader.public_key_size.toInt())
fis.read(ai.auxBlob!!.pubkey!!.pubkey)
log.debug("Parsed Pub Key: " + Hex.encodeHexString(ai.auxBlob!!.pubkey!!.pubkey))
}
}
if (vbMetaHeader.public_key_metadata_size > 0) {
if (vbMetaHeader.public_key_metadata_size > 0U) {
FileInputStream(image_file).use { fis ->
fis.skip(vbMetaOffset)
fis.skip(vbMetaOffset.toLong())
fis.skip(Header.SIZE.toLong())
fis.skip(vbMetaHeader.public_key_metadata_offset)
fis.skip(vbMetaHeader.public_key_metadata_offset.toLong())
val ba = ByteArray(vbMetaHeader.public_key_metadata_size.toInt())
fis.read(ba)
log.debug("Parsed Pub Key Metadata: " + Hex.encodeHexString(ba))
}
}
if (vbMetaHeader.authentication_data_block_size > 0) {
if (vbMetaHeader.authentication_data_block_size > 0U) {
FileInputStream(image_file).use { fis ->
fis.skip(vbMetaOffset)
fis.skip(vbMetaOffset.toLong())
fis.skip(Header.SIZE.toLong())
fis.skip(vbMetaHeader.hash_offset)
fis.skip(vbMetaHeader.hash_offset.toLong())
val ba = ByteArray(vbMetaHeader.hash_size.toInt())
fis.read(ba)
log.debug("Parsed Auth Hash (Header & Aux Blob): " + Hex.encodeHexString(ba))
@ -382,7 +382,7 @@ class Avb {
//3 - whole aux blob
var auxBlob = byteArrayOf()
if (ai.header!!.auxiliary_data_block_size > 0) {
if (ai.header!!.auxiliary_data_block_size > 0U) {
if (encodedKey.contentEquals(ai.auxBlob!!.pubkey!!.pubkey)) {
log.info("Using the same key as original vbmeta")
} else {
@ -395,24 +395,24 @@ class Avb {
//1 - whole header blob
val headerBlob = ai.header!!.apply {
auxiliary_data_block_size = auxBlob.size.toLong()
auxiliary_data_block_size = auxBlob.size.toULong()
authentication_data_block_size = Helper.round_to_multiple(
(alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64)
(alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64).toULong()
descriptors_offset = 0
descriptors_size = encodedDesc.size.toLong()
descriptors_offset = 0U
descriptors_size = encodedDesc.size.toULong()
hash_offset = 0
hash_size = alg.hash_num_bytes.toLong()
hash_offset = 0U
hash_size = alg.hash_num_bytes.toULong()
signature_offset = alg.hash_num_bytes.toLong()
signature_size = alg.signature_num_bytes.toLong()
signature_offset = alg.hash_num_bytes.toULong()
signature_size = alg.signature_num_bytes.toULong()
public_key_offset = descriptors_size
public_key_size = encodedKey.size.toLong()
public_key_size = encodedKey.size.toULong()
//TODO: support pubkey metadata
public_key_metadata_size = 0
public_key_metadata_size = 0U
public_key_metadata_offset = public_key_offset + public_key_size
}.encode()
@ -430,7 +430,7 @@ class Avb {
fun packVbMetaWithPadding(info: AVBInfo? = null) {
val rawBlob = packVbMeta(info)
val paddingSize = Helper.round_to_multiple(rawBlob.size.toLong(), BLOCK_SIZE) - rawBlob.size
val paddedBlob = Helper.join(rawBlob, Struct("${paddingSize}x").pack(null))
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: vbmeta.img.signed")
Files.write(Paths.get("vbmeta.img.signed"), paddedBlob, StandardOpenOption.CREATE)
@ -438,8 +438,8 @@ class Avb {
companion object {
private val log = LoggerFactory.getLogger(Avb::class.java)
const val AVB_VERSION_MAJOR = 1
const val AVB_VERSION_MINOR = 1
const val AVB_VERSION_MAJOR = 1U
const val AVB_VERSION_MINOR = 1U
const val AVB_VERSION_SUB = 0
fun getJsonFileName(image_file: String): String {

@ -1,7 +1,8 @@
package cfig
import cfig.io.Struct
import cfig.io.Struct3
import com.google.common.math.BigIntegerMath
import com.sun.org.apache.xml.internal.utils.UnImplNode
import org.apache.commons.codec.binary.Hex
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
import org.apache.commons.compress.compressors.gzip.GzipParameters
@ -42,6 +43,14 @@ class Helper {
return baos.toByteArray()
}
fun toHexString(inData: UByteArray): String {
val sb = StringBuilder()
for (i in inData.indices) {
sb.append(Integer.toString((inData[i].toInt().and(0xff)) + 0x100, 16).substring(1))
}
return sb.toString()
}
fun toHexString(inData: ByteArray): String {
val sb = StringBuilder()
for (i in inData.indices) {
@ -169,6 +178,15 @@ class Helper {
}
}
fun round_to_multiple(size: UInt, page: UInt): UInt {
val remainder = size % page
return if (remainder == 0U) {
size
} else {
size + page - remainder
}
}
fun round_to_multiple(size: Long, page: Int): Long {
val remainder = size % page
return if (remainder == 0L) {
@ -201,7 +219,7 @@ class Helper {
log.debug("BB: " + numBits / 8 + ", mod_len: " + rsa.modulus.toByteArray().size + ", rrmodn = " + rrModn.toByteArray().size)
val unsignedModulo = rsa.modulus.toByteArray().sliceArray(1..numBits / 8) //remove sign byte
log.debug("unsigned modulo: " + Hex.encodeHexString(unsignedModulo))
val ret = Struct("!II${numBits / 8}b${numBits / 8}b").pack(
val ret = Struct3("!II${numBits / 8}b${numBits / 8}b").pack(
numBits,
n0inv,
unsignedModulo,

@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
import org.apache.commons.exec.PumpStreamHandler
import org.junit.Assert
import org.junit.Assert.assertTrue
import org.slf4j.LoggerFactory
import java.io.*
@ -49,6 +50,11 @@ class Packer {
return md.digest()
}
private fun writePaddedFile(inBF: ByteBuffer, srcFile: String, padding: UInt) {
Assert.assertTrue(padding < Int.MAX_VALUE.toUInt())
writePaddedFile(inBF, srcFile, padding.toInt())
}
private fun writePaddedFile(inBF: ByteBuffer, srcFile: String, padding: Int) {
FileInputStream(srcFile).use { iS ->
var byteRead: Int
@ -64,6 +70,11 @@ class Packer {
}
}
private fun padFile(inBF: ByteBuffer, padding: UInt) {
Assert.assertTrue(padding < Int.MAX_VALUE.toUInt())
padFile(inBF, padding.toInt())
}
private fun padFile(inBF: ByteBuffer, padding: Int) {
val pad = padding - (inBF.position() and padding - 1) and padding - 1
inBF.put(ByteArray(pad))
@ -77,16 +88,16 @@ class Packer {
bf.order(ByteOrder.LITTLE_ENDIAN)
writePaddedFile(bf, param.kernel, info2.pageSize)
if (info2.ramdiskLength > 0) {
if (info2.ramdiskLength > 0U) {
writePaddedFile(bf, param.ramdisk!!, info2.pageSize)
}
if (info2.secondBootloaderLength > 0) {
if (info2.secondBootloaderLength > 0U) {
writePaddedFile(bf, param.second!!, info2.pageSize)
}
if (info2.recoveryDtboLength > 0) {
if (info2.recoveryDtboLength > 0U) {
writePaddedFile(bf, param.dtbo!!, info2.pageSize)
}
if (info2.dtbLength > 0) {
if (info2.dtbLength > 0U) {
writePaddedFile(bf, param.dtb!!, info2.pageSize)
}
//write
@ -131,7 +142,7 @@ class Packer {
File(cfg.info.output + ".signed2").deleleIfExists()
File("${UnifiedConfig.workDir}ramdisk.img").deleleIfExists()
if (info2.ramdiskLength > 0) {
if (info2.ramdiskLength > 0U) {
if (File(param.ramdisk).exists() && !File(UnifiedConfig.workDir + "root").exists()) {
//do nothing if we have ramdisk.img.gz but no /root
log.warn("Use prebuilt ramdisk file: ${param.ramdisk}")
@ -145,7 +156,7 @@ class Packer {
//write
FileOutputStream(cfg.info.output + ".clear", false).use { fos ->
fos.write(encodedHeader)
fos.write(ByteArray(info2.pageSize - encodedHeader.size))
fos.write(ByteArray(info2.pageSize.toInt() - encodedHeader.size))
}
writeData(info2, cfg.info.output)

@ -56,17 +56,17 @@ class Parser {
fun extractBootImg(fileName: String, info2: BootImgInfo) {
val param = ParamConfig()
if (info2.kernelLength > 0) {
if (info2.kernelLength > 0U) {
Helper.extractFile(fileName,
param.kernel,
info2.kernelPosition.toLong(),
info2.kernelLength.toInt())
log.info(" kernel dumped to: ${param.kernel}, size=${info2.kernelLength / 1024.0 / 1024.0}MB")
log.info(" kernel dumped to: ${param.kernel}, size=${info2.kernelLength.toInt() / 1024.0 / 1024.0}MB")
} else {
throw RuntimeException("bad boot image: no kernel found")
}
if (info2.ramdiskLength > 0) {
if (info2.ramdiskLength > 0U) {
Helper.extractFile(fileName,
param.ramdisk!!,
info2.ramdiskPosition.toLong(),
@ -78,7 +78,7 @@ class Parser {
log.info("no ramdisk found")
}
if (info2.secondBootloaderLength > 0) {
if (info2.secondBootloaderLength > 0U) {
Helper.extractFile(fileName,
param.second!!,
info2.secondBootloaderPosition.toLong(),
@ -88,28 +88,28 @@ class Parser {
log.info("no second bootloader found")
}
if (info2.recoveryDtboLength > 0) {
if (info2.recoveryDtboLength > 0U) {
Helper.extractFile(fileName,
param.dtbo!!,
info2.recoveryDtboPosition.toLong(),
info2.recoveryDtboLength.toInt())
log.info("dtbo dumped to ${param.dtbo}")
} else {
if (info2.headerVersion > 0) {
if (info2.headerVersion > 0U) {
log.info("no recovery dtbo found")
} else {
log.debug("no recovery dtbo for header v0")
}
}
if (info2.dtbLength > 0) {
if (info2.dtbLength > 0U) {
Helper.extractFile(fileName,
param.dtb!!,
info2.dtbPosition.toLong(),
info2.dtbLength.toInt())
log.info("dtb dumped to ${param.dtb}")
} else {
if (info2.headerVersion > 1) {
if (info2.headerVersion > 1U) {
log.info("no dtb found")
} else {
log.debug("no dtb for header v0")

@ -44,7 +44,7 @@ class Signer {
salt = Helper.toHexString(bootDesc.salt),
hash_algorithm = bootDesc.hash_algorithm_str,
partition_name = bootDesc.partition_name,
rollback_index = ai.header!!.rollback_index,
rollback_index = ai.header!!.rollback_index.toLong(),
common_algorithm = alg!!.name,
inReleaseString = ai.header!!.release_string)
//original signer

@ -24,12 +24,12 @@ data class UnifiedConfig(
data class MiscInfo(
var output: String = "",
var headerVersion: Int = 0,
var headerSize: Int = 0,
var headerVersion: UInt = 0U,
var headerSize: UInt = 0U,
var loadBase: String = "",
var tagsOffset: String = "0",
var board: String? = null,
var pageSize: Int = 0,
var pageSize: UInt = 0U,
var cmdline: String = "",
var osVersion: String? = null,
var osPatchLevel: String? = null,
@ -39,36 +39,36 @@ data class UnifiedConfig(
fun toBootImgInfo(): BootImgInfo {
val ret = BootImgInfo()
ret.kernelOffset = this.kernel.loadOffset.removePrefix("0x").toLong(16)
ret.kernelLength = Integer.decode(this.kernel.size).toLong()
ret.kernelOffset = this.kernel.loadOffset.removePrefix("0x").toUInt(16)
ret.kernelLength = Integer.decode(this.kernel.size).toUInt()
ret.kernelOffset = this.kernel.loadOffset.removePrefix("0x").toLong(16)
ret.kernelLength = Integer.decode(this.kernel.size).toLong()
ret.kernelOffset = this.kernel.loadOffset.removePrefix("0x").toUInt(16)
ret.kernelLength = Integer.decode(this.kernel.size).toUInt()
this.ramdisk?.let {
ret.ramdiskOffset = it.loadOffset.removePrefix("0x").toLong(16)
ret.ramdiskLength = it.size.removePrefix("0x").toLong(16)
ret.ramdiskOffset = it.loadOffset.removePrefix("0x").toUInt(16)
ret.ramdiskLength = it.size.removePrefix("0x").toUInt(16)
}
this.secondBootloader?.let {
ret.secondBootloaderOffset = it.loadOffset.removePrefix("0x").toLong(16)
ret.secondBootloaderLength = it.size.removePrefix("0x").toLong(16)
ret.secondBootloaderOffset = it.loadOffset.removePrefix("0x").toUInt(16)
ret.secondBootloaderLength = it.size.removePrefix("0x").toUInt(16)
}
this.recoveryDtbo?.let {
ret.recoveryDtboOffset = it.loadOffset.removePrefix("0x").toLong(16)
ret.recoveryDtboLength = it.size.removePrefix("0x").toLong(16)
ret.recoveryDtboOffset = it.loadOffset.removePrefix("0x").toULong(16)
ret.recoveryDtboLength = it.size.removePrefix("0x").toUInt(16)
}
this.dtb?.let {
ret.dtbOffset = it.loadOffset.removePrefix("0x").toLong(16)
ret.dtbLength = it.size.removePrefix("0x").toLong(16)
ret.dtbOffset = it.loadOffset.removePrefix("0x").toULong(16)
ret.dtbLength = it.size.removePrefix("0x").toUInt(16)
}
ret.headerSize = this.info.headerSize.toLong()
ret.headerSize = this.info.headerSize
ret.headerVersion = this.info.headerVersion
this.info.board?.let { ret.board = it }
ret.tagsOffset = this.info.tagsOffset.removePrefix("0x").toLong(16)
ret.tagsOffset = this.info.tagsOffset.removePrefix("0x").toUInt(16)
ret.cmdline = this.info.cmdline
ret.osVersion = this.info.osVersion
ret.osPatchLevel = this.info.osPatchLevel
@ -88,52 +88,52 @@ data class UnifiedConfig(
val ret = UnifiedConfig()
val param = ParamConfig()
ret.kernel.file = param.kernel
ret.kernel.loadOffset = "0x${java.lang.Long.toHexString(info.kernelOffset)}"
ret.kernel.loadOffset = "0x${Integer.toHexString(info.kernelOffset.toInt())}"
ret.kernel.size = "0x${Integer.toHexString(info.kernelLength.toInt())}"
ret.kernel.position = "0x${Integer.toHexString(info.kernelPosition)}"
ret.kernel.position = "0x${Integer.toHexString(info.kernelPosition.toInt())}"
ret.ramdisk = CommArgs()
ret.ramdisk!!.loadOffset = "0x${java.lang.Long.toHexString(info.ramdiskOffset)}"
ret.ramdisk!!.loadOffset = "0x${Integer.toHexString(info.ramdiskOffset.toInt())}"
ret.ramdisk!!.size = "0x${Integer.toHexString(info.ramdiskLength.toInt())}"
ret.ramdisk!!.position = "0x${Integer.toHexString(info.ramdiskPosition)}"
if (info.ramdiskLength > 0) {
ret.ramdisk!!.position = "0x${java.lang.Long.toHexString(info.ramdiskPosition.toInt().toLong())}"
if (info.ramdiskLength > 0U) {
ret.ramdisk!!.file = param.ramdisk
}
ret.secondBootloader = CommArgs()
ret.secondBootloader!!.loadOffset = "0x${java.lang.Long.toHexString(info.secondBootloaderOffset)}"
ret.secondBootloader!!.loadOffset = "0x${Integer.toHexString(info.secondBootloaderOffset.toInt())}"
ret.secondBootloader!!.size = "0x${Integer.toHexString(info.secondBootloaderLength.toInt())}"
ret.secondBootloader!!.position = "0x${Integer.toHexString(info.secondBootloaderPosition)}"
if (info.secondBootloaderLength > 0) {
ret.secondBootloader!!.position = "0x${Integer.toHexString(info.secondBootloaderPosition.toInt())}"
if (info.secondBootloaderLength > 0U) {
ret.secondBootloader!!.file = param.second
}
if (info.headerVersion > 0) {
if (info.headerVersion > 0U) {
ret.recoveryDtbo = CommArgs()
if (info.recoveryDtboLength > 0) {
if (info.recoveryDtboLength > 0U) {
ret.recoveryDtbo!!.file = param.dtbo
}
ret.recoveryDtbo!!.loadOffset = "0x${java.lang.Long.toHexString(info.recoveryDtboOffset)}"
ret.recoveryDtbo!!.loadOffset = "0x${java.lang.Long.toHexString(info.recoveryDtboOffset.toLong())}"
ret.recoveryDtbo!!.size = "0x${Integer.toHexString(info.recoveryDtboLength.toInt())}"
ret.recoveryDtbo!!.position = "0x${Integer.toHexString(info.recoveryDtboPosition)}"
ret.recoveryDtbo!!.position = "0x${java.lang.Long.toHexString(info.recoveryDtboPosition.toLong())}"
}
if (info.headerVersion > 1) {
if (info.headerVersion > 1U) {
ret.dtb = CommArgs()
if (info.dtbLength > 0) {
if (info.dtbLength > 0U) {
ret.dtb!!.file = param.dtb
}
ret.dtb!!.loadOffset = "0x${java.lang.Long.toHexString(info.dtbOffset)}"
ret.dtb!!.loadOffset = "0x${java.lang.Integer.toHexString(info.dtbOffset.toInt())}"
ret.dtb!!.size = "0x${Integer.toHexString(info.dtbLength.toInt())}"
ret.dtb!!.position = "0x${Integer.toHexString(info.dtbPosition)}"
ret.dtb!!.position = "0x${java.lang.Long.toHexString(info.dtbPosition.toLong())}"
}
//ret.info.output = //unknown
ret.info.headerSize = info.headerSize.toInt()
ret.info.headerSize = info.headerSize
ret.info.headerVersion = info.headerVersion
ret.info.loadBase = "0x${java.lang.Long.toHexString(0)}"
ret.info.board = if (info.board.isBlank()) null else info.board
ret.info.tagsOffset = "0x${java.lang.Long.toHexString(info.tagsOffset)}"
ret.info.tagsOffset = "0x${java.lang.Long.toHexString(info.tagsOffset.toLong())}"
ret.info.cmdline = info.cmdline
ret.info.osVersion = info.osVersion
ret.info.osPatchLevel = info.osPatchLevel

@ -1,7 +1,7 @@
package avb
data class AuthBlob(
var offset: Long = 0L,
var size: Long = 0L,
var offset: ULong = 0U,
var size: ULong = 0U,
var hash: String? = null,
var signature: String? = null)

@ -3,7 +3,7 @@ package avb
import avb.alg.Algorithm
import avb.desc.*
import cfig.Helper
import cfig.io.Struct
import cfig.io.Struct3
import org.junit.Assert
import org.slf4j.LoggerFactory
import java.nio.file.Files
@ -56,7 +56,7 @@ data class AuxBlob(
this.pubkey?.let { sumOfSize += it.pubkey.size }
this.pubkeyMeta?.let { sumOfSize += it.pkmd.size }
val auxSize = Helper.round_to_multiple(sumOfSize.toLong(), 64)
return Struct("${auxSize}b").pack(
return Struct3("${auxSize}b").pack(
Helper.joinWithNulls(encodedDesc, this.pubkey?.pubkey, this.pubkeyMeta?.pkmd))
}

@ -2,7 +2,7 @@ package avb
import avb.alg.Algorithms
import cfig.Helper
import cfig.io.Struct
import cfig.io.Struct3
import org.slf4j.LoggerFactory
import java.security.MessageDigest
@ -15,7 +15,7 @@ class Blob {
val auxSize = Helper.round_to_multiple(
(encodedDesc.size + encodedKey.size + pkmdBlob.size).toLong(),
64)
return Struct("${auxSize}b").pack(Helper.join(encodedDesc, encodedKey, pkmdBlob))
return Struct3("${auxSize}b").pack(Helper.join(encodedDesc, encodedKey, pkmdBlob))
}
fun getAuthBlob(header_data_blob: ByteArray,
@ -40,7 +40,7 @@ class Blob {
binarySignature = Helper.rawSign(alg.defaultKey.replace(".pem", ".pk8"), Helper.join(alg.padding, binaryHash))
}
val authData = Helper.join(binaryHash, binarySignature)
return Helper.join(authData, Struct("${authBlockSize - authData.size}x").pack(0))
return Helper.join(authData, Struct3("${authBlockSize - authData.size}x").pack(0))
}
}
}

@ -1,6 +1,6 @@
package avb
import cfig.io.Struct
import cfig.io.Struct3
import org.junit.Assert
import java.io.InputStream
/*
@ -22,41 +22,41 @@ https://github.com/cfig/Android_boot_image_editor/blob/master/doc/layout.md#32-a
*/
data class Footer constructor(
var versionMajor: Long = FOOTER_VERSION_MAJOR,
var versionMinor: Long = FOOTER_VERSION_MINOR,
var originalImageSize: Long = 0L,
var vbMetaOffset: Long = 0L,
var vbMetaSize: Long = 0L
var versionMajor: UInt = FOOTER_VERSION_MAJOR,
var versionMinor: UInt = FOOTER_VERSION_MINOR,
var originalImageSize: ULong = 0U,
var vbMetaOffset: ULong = 0U,
var vbMetaSize: ULong = 0U
) {
companion object {
const val MAGIC = "AVBf"
const val SIZE = 64
private const val RESERVED = 28
const val FOOTER_VERSION_MAJOR = 1L
const val FOOTER_VERSION_MINOR = 0L
const val FOOTER_VERSION_MAJOR = 1U
const val FOOTER_VERSION_MINOR = 0U
private const val FORMAT_STRING = "!4s2L3Q${RESERVED}x"
init {
Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize())
Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize())
}
}
@Throws(IllegalArgumentException::class)
constructor(iS: InputStream) : this() {
val info = Struct(FORMAT_STRING).unpack(iS)
val info = Struct3(FORMAT_STRING).unpack(iS)
Assert.assertEquals(7, info.size)
if (!MAGIC.toByteArray().contentEquals(info[0] as ByteArray)) {
if (MAGIC != (info[0] as String)) {
throw IllegalArgumentException("stream doesn't look like valid AVB Footer")
}
versionMajor = info[1] as Long
versionMinor = info[2] as Long
originalImageSize = info[3] as Long
vbMetaOffset = info[4] as Long
vbMetaSize = info[5] as Long
versionMajor = info[1] as UInt
versionMinor = info[2] as UInt
originalImageSize = info[3] as ULong
vbMetaOffset = info[4] as ULong
vbMetaSize = info[5] as ULong
}
fun encode(): ByteArray {
return Struct(FORMAT_STRING).pack(Footer.MAGIC.toByteArray(),
return Struct3(FORMAT_STRING).pack(MAGIC,
this.versionMajor,
this.versionMinor,
this.originalImageSize,

@ -2,61 +2,61 @@ package avb
import cfig.Avb
import cfig.Helper
import cfig.io.Struct
import cfig.io.Struct3
import org.junit.Assert
import java.io.InputStream
//avbtool::AvbVBMetaHeader
data class Header(
var required_libavb_version_major: Int = Avb.AVB_VERSION_MAJOR,
var required_libavb_version_minor: Int = 0,
var authentication_data_block_size: Long = 0L,
var auxiliary_data_block_size: Long = 0L,
var algorithm_type: Long = 0L,
var hash_offset: Long = 0L,
var hash_size: Long = 0L,
var signature_offset: Long = 0L,
var signature_size: Long = 0L,
var public_key_offset: Long = 0L,
var public_key_size: Long = 0L,
var public_key_metadata_offset: Long = 0L,
var public_key_metadata_size: Long = 0L,
var descriptors_offset: Long = 0L,
var descriptors_size: Long = 0L,
var rollback_index: Long = 0L,
var flags: Long = 0,
var required_libavb_version_major: UInt = Avb.AVB_VERSION_MAJOR,
var required_libavb_version_minor: UInt = 0U,
var authentication_data_block_size: ULong = 0U,
var auxiliary_data_block_size: ULong = 0U,
var algorithm_type: UInt = 0U,
var hash_offset: ULong = 0U,
var hash_size: ULong = 0U,
var signature_offset: ULong = 0U,
var signature_size: ULong = 0U,
var public_key_offset: ULong = 0U,
var public_key_size: ULong = 0U,
var public_key_metadata_offset: ULong = 0U,
var public_key_metadata_size: ULong = 0U,
var descriptors_offset: ULong = 0U,
var descriptors_size: ULong = 0U,
var rollback_index: ULong = 0U,
var flags: UInt = 0U,
var release_string: String = "avbtool ${Avb.AVB_VERSION_MAJOR}.${Avb.AVB_VERSION_MINOR}.${Avb.AVB_VERSION_SUB}") {
@Throws(IllegalArgumentException::class)
constructor(iS: InputStream) : this() {
val info = Struct(FORMAT_STRING).unpack(iS)
val info = Struct3(FORMAT_STRING).unpack(iS)
Assert.assertEquals(22, info.size)
if (!(info[0] as ByteArray).contentEquals(magic.toByteArray())) {
if (info[0] != magic) {
throw IllegalArgumentException("stream doesn't look like valid VBMeta Header")
}
this.required_libavb_version_major = (info[1] as Long).toInt()
this.required_libavb_version_minor = (info[2] as Long).toInt()
this.authentication_data_block_size = info[3] as Long
this.auxiliary_data_block_size = info[4] as Long
this.algorithm_type = info[5] as Long
this.hash_offset = info[6] as Long
this.hash_size = info[7] as Long
this.signature_offset = info[8] as Long
this.signature_size = info[9] as Long
this.public_key_offset = info[10] as Long
this.public_key_size = info[11] as Long
this.public_key_metadata_offset = info[12] as Long
this.public_key_metadata_size = info[13] as Long
this.descriptors_offset = info[14] as Long
this.descriptors_size = info[15] as Long
this.rollback_index = info[16] as Long
this.flags = info[17] as Long
this.required_libavb_version_major = info[1] as UInt
this.required_libavb_version_minor = info[2] as UInt
this.authentication_data_block_size = info[3] as ULong
this.auxiliary_data_block_size = info[4] as ULong
this.algorithm_type = info[5] as UInt
this.hash_offset = info[6] as ULong
this.hash_size = info[7] as ULong
this.signature_offset = info[8] as ULong
this.signature_size = info[9] as ULong
this.public_key_offset = info[10] as ULong
this.public_key_size = info[11] as ULong
this.public_key_metadata_offset = info[12] as ULong
this.public_key_metadata_size = info[13] as ULong
this.descriptors_offset = info[14] as ULong
this.descriptors_size = info[15] as ULong
this.rollback_index = info[16] as ULong
this.flags = info[17] as UInt
//padding: info[18]
this.release_string = Helper.toCString(info[19] as ByteArray)
this.release_string = info[19] as String
}
fun encode(): ByteArray {
return Struct(FORMAT_STRING).pack(
magic.toByteArray(),
return Struct3(FORMAT_STRING).pack(
magic,
this.required_libavb_version_major, this.required_libavb_version_minor,
this.authentication_data_block_size, this.auxiliary_data_block_size,
this.algorithm_type,
@ -68,12 +68,12 @@ data class Header(
this.rollback_index,
this.flags,
null, //${REVERSED0}x
this.release_string.toByteArray(), //47s
this.release_string, //47s
null, //x
null) //${REVERSED}x
}
fun bump_required_libavb_version_minor(minor: Int) {
fun bump_required_libavb_version_minor(minor: UInt) {
this.required_libavb_version_minor = maxOf(required_libavb_version_minor, minor)
}
@ -85,7 +85,7 @@ data class Header(
const val FORMAT_STRING = ("!4s2L2QL11QL${REVERSED0}x47sx" + "${REVERSED}x")
init {
Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize())
Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize())
}
}
}

@ -1,6 +1,6 @@
package avb.alg
import cfig.io.Struct
import cfig.io.Struct3
class Algorithms {
companion object {
@ -28,7 +28,7 @@ class Algorithms {
hash_num_bytes = 32,
signature_num_bytes = 256,
public_key_num_bytes = 8 + 2 * 2048 / 8,
padding = Struct("2b202x1b19b").pack(
padding = Struct3("2b202x1b19B").pack(
byteArrayOf(0x00, 0x01),
0xff,
byteArrayOf(0x00),
@ -44,7 +44,7 @@ class Algorithms {
hash_num_bytes = 32,
signature_num_bytes = 512,
public_key_num_bytes = 8 + 2 * 4096 / 8,
padding = Struct("2b458x1x19b").pack(
padding = Struct3("2b458x1x19B").pack(
byteArrayOf(0x00, 0x01),
0xff,
0x00,
@ -62,7 +62,7 @@ class Algorithms {
hash_num_bytes = 32,
signature_num_bytes = 1024,
public_key_num_bytes = 8 + 2 * 8192 / 8,
padding = Struct("2b970x1x19b").pack(
padding = Struct3("2b970x1x19B").pack(
intArrayOf(0x00, 0x01),
0xff,
0x00,
@ -78,7 +78,7 @@ class Algorithms {
hash_num_bytes = 64,
signature_num_bytes = 256,
public_key_num_bytes = 8 + 2 * 2048 / 8,
padding = Struct("2b170x1x19b").pack(
padding = Struct3("2b170x1x19B").pack(
intArrayOf(0x00, 0x01),
0xff,
0x00,
@ -94,7 +94,7 @@ class Algorithms {
hash_num_bytes = 64,
signature_num_bytes = 512,
public_key_num_bytes = 8 + 2 * 4096 / 8,
padding = Struct("2b426x1x19b").pack(
padding = Struct3("2b426x1x19B").pack(
intArrayOf(0x00, 0x01),
0xff,
0x00,
@ -111,7 +111,7 @@ class Algorithms {
signature_num_bytes = 1024,
public_key_num_bytes = 8 + 2 * 8192 / 8,
padding = Struct("2b938x1x19b").pack(
padding = Struct3("2b938x1x19B").pack(
intArrayOf(0x00, 0x01),
0xff,
0x00,

@ -1,61 +1,61 @@
package avb.desc
import cfig.Helper
import cfig.io.Struct
import cfig.io.Struct3
import java.io.InputStream
import java.security.MessageDigest
import java.util.*
class ChainPartitionDescriptor(
var rollback_index_location: Long = 0,
var partition_name_len: Long = 0,
var public_key_len: Long = 0,
var rollback_index_location: UInt = 0U,
var partition_name_len: UInt = 0U,
var public_key_len: UInt = 0U,
var partition_name: String = "",
var pubkey: ByteArray = byteArrayOf(),
var pubkey_sha1: String = ""
) : Descriptor(TAG, 0, 0) {
) : Descriptor(TAG, 0U, 0) {
override fun encode(): ByteArray {
this.partition_name_len = this.partition_name.length.toLong()
this.public_key_len = this.pubkey.size.toLong()
this.num_bytes_following = SIZE + this.partition_name_len + this.public_key_len - 16
val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following, 8)
this.partition_name_len = this.partition_name.length.toUInt()
this.public_key_len = this.pubkey.size.toUInt()
this.num_bytes_following = (SIZE.toUInt() + this.partition_name_len + this.public_key_len - 16U).toULong()
val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following.toLong(), 8).toULong()
val padding_size = nbf_with_padding - this.num_bytes_following
val desc = Struct(FORMAT_STRING + "${RESERVED}s").pack(
val desc = Struct3(FORMAT_STRING + "${RESERVED}x").pack(
TAG,
nbf_with_padding,
nbf_with_padding.toULong(),
this.rollback_index_location,
this.partition_name.length,
this.partition_name.length.toUInt(),
this.public_key_len,
null)
val padding = Struct("${padding_size}s").pack(null)
val padding = Struct3("${padding_size}x").pack(null)
return Helper.join(desc, this.partition_name.toByteArray(), this.pubkey, padding)
}
companion object {
const val TAG = 4L
const val TAG: ULong = 4U
const val RESERVED = 64
const val SIZE = 28L + RESERVED
const val FORMAT_STRING = "!2Q3L"
}
constructor(data: InputStream, seq: Int = 0) : this() {
if (SIZE - RESERVED != Struct(FORMAT_STRING).calcSize().toLong()) {
if (SIZE - RESERVED != Struct3(FORMAT_STRING).calcSize()!!.toLong()) {
throw RuntimeException()
}
this.sequence = seq
val info = Struct(FORMAT_STRING + "${RESERVED}s").unpack(data)
this.tag = info[0] as Long
this.num_bytes_following = info[1] as Long
this.rollback_index_location = info[2] as Long
this.partition_name_len = info[3] as Long
this.public_key_len = info[4] as Long
val info = Struct3(FORMAT_STRING + "${RESERVED}s").unpack(data)
this.tag = info[0] as ULong
this.num_bytes_following = info[1] as ULong
this.rollback_index_location = info[2] as UInt
this.partition_name_len = info[3] as UInt
this.public_key_len = info[4] as UInt
val expectedSize = Helper.round_to_multiple(
SIZE - 16 + this.partition_name_len + this.public_key_len, 8)
if (this.tag != TAG || this.num_bytes_following != expectedSize) {
SIZE.toUInt() - 16U + this.partition_name_len + this.public_key_len, 8U)
if (this.tag != TAG || this.num_bytes_following != expectedSize.toULong()) {
throw IllegalArgumentException("Given data does not look like a chain/delegation descriptor")
}
val info2 = Struct("${this.partition_name_len}s${this.public_key_len}s").unpack(data)
this.partition_name = Helper.toCString(info2[0] as ByteArray)
val info2 = Struct3("${this.partition_name_len}s${this.public_key_len}b").unpack(data)
this.partition_name = info2[0] as String
this.pubkey = info2[1] as ByteArray
val md = MessageDigest.getInstance("SHA1")
md.update(this.pubkey)

@ -1,5 +1,5 @@
package avb.desc
abstract class Descriptor(var tag: Long, var num_bytes_following: Long, var sequence: Int = 0) {
abstract class Descriptor(var tag: ULong, var num_bytes_following: ULong, var sequence: Int = 0) {
abstract fun encode(): ByteArray
}

@ -1,50 +1,51 @@
package avb.desc
import cfig.Helper
import cfig.io.Struct
import cfig.io.Struct3
import org.junit.Assert
import java.io.File
import java.io.InputStream
import java.security.MessageDigest
class HashDescriptor(var image_size: Long = 0L,
var hash_algorithm: ByteArray = byteArrayOf(),
class HashDescriptor(var image_size: ULong = 0U,
var hash_algorithm: String = "",
var hash_algorithm_str: String = "",
var partition_name_len: Long = 0L,
var salt_len: Long = 0L,
var digest_len: Long = 0L,
var flags: Long = 0L,
var partition_name_len: UInt = 0U,
var salt_len: UInt = 0U,
var digest_len: UInt = 0U,
var flags: UInt = 0U,
var partition_name: String = "",
var salt: ByteArray = byteArrayOf(),
var digest: ByteArray = byteArrayOf()) : Descriptor(TAG, 0, 0) {
var digest: ByteArray = byteArrayOf()) : Descriptor(TAG, 0U, 0) {
constructor(data: InputStream, seq: Int = 0) : this() {
val info = Struct(FORMAT_STRING).unpack(data)
this.tag = info[0] as Long
this.num_bytes_following = info[1] as Long
this.image_size = info[2] as Long
this.hash_algorithm = info[3] as ByteArray
this.partition_name_len = info[4] as Long
this.salt_len = info[5] as Long
this.digest_len = info[6] as Long
this.flags = info[7] as Long
val info = Struct3(FORMAT_STRING).unpack(data)
this.tag = info[0] as ULong
this.num_bytes_following = info[1] as ULong
this.image_size = info[2] as ULong
this.hash_algorithm = info[3] as String
this.partition_name_len = info[4] as UInt
this.salt_len = info[5] as UInt
this.digest_len = info[6] as UInt
this.flags = info[7] as UInt
this.sequence = seq
val expectedSize = Helper.round_to_multiple(SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
val expectedSize = Helper.round_to_multiple(
SIZE - 16 + (partition_name_len + salt_len + digest_len).toLong(), 8).toULong()
if (this.tag != TAG || expectedSize != this.num_bytes_following) {
throw IllegalArgumentException("Given data does not look like a |hash| descriptor")
}
val payload = Struct("${this.partition_name_len}s${this.salt_len}b${this.digest_len}b").unpack(data)
val payload = Struct3("${this.partition_name_len}s${this.salt_len}b${this.digest_len}b").unpack(data)
Assert.assertEquals(3, payload.size)
this.partition_name = Helper.toCString(payload[0] as ByteArray)
this.partition_name = payload[0] as String
this.salt = payload[1] as ByteArray
this.digest = payload[2] as ByteArray
this.hash_algorithm_str = Helper.toCString(this.hash_algorithm)
this.hash_algorithm_str = this.hash_algorithm
}
override fun encode(): ByteArray {
val payload_bytes_following = SIZE + this.partition_name.length + this.salt.size + this.digest.size - 16L
this.num_bytes_following = Helper.round_to_multiple(payload_bytes_following, 8)
val padding_size = num_bytes_following - payload_bytes_following
val desc = Struct(FORMAT_STRING).pack(
this.num_bytes_following = Helper.round_to_multiple(payload_bytes_following, 8).toULong()
val padding_size = num_bytes_following - payload_bytes_following.toUInt()
val desc = Struct3(FORMAT_STRING).pack(
TAG,
this.num_bytes_following,
this.image_size,
@ -54,7 +55,7 @@ class HashDescriptor(var image_size: Long = 0L,
this.digest.size,
this.flags,
null)
val padding = Struct("${padding_size}x").pack(null)
val padding = Struct3("${padding_size}x").pack(null)
return Helper.join(desc, partition_name.toByteArray(), this.salt, this.digest, padding)
}
@ -66,13 +67,13 @@ class HashDescriptor(var image_size: Long = 0L,
}
companion object {
const val TAG = 2L
const val TAG: ULong = 2U
private const val RESERVED = 60
private const val SIZE = 72 + RESERVED
private const val FORMAT_STRING = "!3Q32s4L${RESERVED}s"
private const val FORMAT_STRING = "!3Q32s4L${RESERVED}x"
}
override fun toString(): String {
return "HashDescriptor(TAG=$TAG, image_size=$image_size, hash_algorithm=${Helper.toCString(hash_algorithm)}, flags=$flags, partition_name='$partition_name', salt=${Helper.toHexString(salt)}, digest=${Helper.toHexString(digest)})"
return "HashDescriptor(TAG=$TAG, image_size=$image_size, hash_algorithm=$hash_algorithm, flags=$flags, partition_name='$partition_name', salt=${Helper.toHexString(salt)}, digest=${Helper.toHexString(digest)})"
}
}

@ -1,63 +1,63 @@
package avb.desc
import cfig.Helper
import cfig.io.Struct
import cfig.io.Struct3
import org.slf4j.LoggerFactory
import java.io.InputStream
import java.util.*
class HashTreeDescriptor(
var dm_verity_version: Long = 0L,
var image_size: Long = 0L,
var tree_offset: Long = 0L,
var tree_size: Long = 0L,
var data_block_size: Long = 0L,
var hash_block_size: Long = 0L,
var fec_num_roots: Long = 0L,
var fec_offset: Long = 0L,
var fec_size: Long = 0L,
var dm_verity_version: UInt = 0u,
var image_size: ULong = 0UL,
var tree_offset: ULong = 0UL,
var tree_size: ULong = 0UL,
var data_block_size: UInt = 0U,
var hash_block_size: UInt = 0U,
var fec_num_roots: UInt = 0U,
var fec_offset: ULong = 0U,
var fec_size: ULong = 0U,
var hash_algorithm: String = "",
var partition_name: String = "",
var salt: ByteArray = byteArrayOf(),
var root_digest: ByteArray = byteArrayOf(),
var flags: Long = 0L) : Descriptor(TAG, 0, 0) {
var flags: UInt = 0U) : Descriptor(TAG, 0U, 0) {
constructor(data: InputStream, seq: Int = 0) : this() {
this.sequence = seq
val info = Struct(FORMAT_STRING).unpack(data)
this.tag = info[0] as Long
this.num_bytes_following = info[1] as Long
this.dm_verity_version = info[2] as Long
this.image_size = info[3] as Long
this.tree_offset = info[4] as Long
this.tree_size = info[5] as Long
this.data_block_size = info[6] as Long
this.hash_block_size = info[7] as Long
this.fec_num_roots = info[8] as Long
this.fec_offset = info[9] as Long
this.fec_size = info[10] as Long
this.hash_algorithm = Helper.toCString(info[11] as ByteArray)
val partition_name_len = info[12] as Long
val salt_len = info[13] as Long
val root_digest_len = info[14] as Long
this.flags = info[15] as Long
val expectedSize = Helper.round_to_multiple(SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
if (this.tag != TAG || this.num_bytes_following != expectedSize) {
val info = Struct3(FORMAT_STRING).unpack(data)
this.tag = info[0] as ULong
this.num_bytes_following = info[1] as ULong
this.dm_verity_version = info[2] as UInt
this.image_size = info[3] as ULong
this.tree_offset = info[4] as ULong
this.tree_size = info[5] as ULong
this.data_block_size = info[6] as UInt
this.hash_block_size = info[7] as UInt
this.fec_num_roots = info[8] as UInt
this.fec_offset = info[9] as ULong
this.fec_size = info[10] as ULong
this.hash_algorithm = info[11] as String
val partition_name_len = info[12] as UInt
val salt_len = info[13] as UInt
val root_digest_len = info[14] as UInt
this.flags = info[15] as UInt
val expectedSize = Helper.round_to_multiple(SIZE.toUInt() - 16U + partition_name_len + salt_len + root_digest_len, 8U)
if (this.tag != TAG || this.num_bytes_following != expectedSize.toULong()) {
throw IllegalArgumentException("Given data does not look like a hashtree descriptor")
}
val info2 = Struct("${partition_name_len}s${salt_len}s${root_digest_len}s").unpack(data)
this.partition_name = Helper.toCString(info2[0] as ByteArray)
val info2 = Struct3("${partition_name_len}s${salt_len}b${root_digest_len}b").unpack(data)
this.partition_name = info2[0] as String
this.salt = info2[1] as ByteArray
this.root_digest = info2[2] as ByteArray
}
override fun encode(): ByteArray {
this.num_bytes_following = SIZE + this.partition_name.length + this.salt.size + this.root_digest.size - 16
val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following, 8)
val padding_size = nbf_with_padding - this.num_bytes_following
val desc = Struct(FORMAT_STRING).pack(
this.num_bytes_following = (SIZE + this.partition_name.length + this.salt.size + this.root_digest.size - 16).toULong()
val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following.toLong(), 8)
val padding_size = nbf_with_padding - this.num_bytes_following.toLong()
val desc = Struct3(FORMAT_STRING).pack(
TAG,
nbf_with_padding,
nbf_with_padding.toULong(),
this.dm_verity_version,
this.image_size,
this.tree_offset,
@ -67,13 +67,13 @@ class HashTreeDescriptor(
this.fec_num_roots,
this.fec_offset,
this.fec_size,
this.hash_algorithm.toByteArray(),
this.hash_algorithm,
this.partition_name.length,
this.salt.size,
this.root_digest.size,
this.flags,
null)
val padding = Struct("${padding_size}x").pack(null)
val padding = Struct3("${padding_size}x").pack(null)
return Helper.join(desc, this.partition_name.toByteArray(), this.salt, this.root_digest, padding)
}
@ -82,10 +82,10 @@ class HashTreeDescriptor(
}
companion object {
const val TAG = 1L
const val TAG: ULong = 1U
private const val RESERVED = 60L
private const val SIZE = 120 + RESERVED
private const val FORMAT_STRING = "!2QL3Q3L2Q32s4L${RESERVED}s"
private const val FORMAT_STRING = "!2QL3Q3L2Q32s4L${RESERVED}x"
private val log = LoggerFactory.getLogger(HashTreeDescriptor::class.java)
}
}

@ -1,51 +1,51 @@
package avb.desc
import cfig.Helper
import cfig.io.Struct
import cfig.io.Struct3
import org.junit.Assert
import java.io.InputStream
class KernelCmdlineDescriptor(
var flags: Long = 0,
var cmdlineLength: Long = 0,
var cmdline: String = "") : Descriptor(TAG, 0, 0) {
var flags: UInt = 0U,
var cmdlineLength: UInt = 0U,
var cmdline: String = "") : Descriptor(TAG, 0U, 0) {
@Throws(IllegalArgumentException::class)
constructor(data: InputStream, seq: Int = 0) : this() {
val info = Struct(FORMAT_STRING).unpack(data)
this.tag = info[0] as Long
this.num_bytes_following = info[1] as Long
this.flags = info[2] as Long
this.cmdlineLength = info[3] as Long
val info = Struct3(FORMAT_STRING).unpack(data)
this.tag = info[0] as ULong
this.num_bytes_following = info[1] as ULong
this.flags = info[2] as UInt
this.cmdlineLength = info[3] as UInt
this.sequence = seq
val expectedSize = Helper.round_to_multiple(SIZE - 16 + this.cmdlineLength, 8)
if ((this.tag != TAG) || (this.num_bytes_following != expectedSize)) {
val expectedSize = Helper.round_to_multiple(SIZE.toUInt() - 16U + this.cmdlineLength, 8U)
if ((this.tag != TAG) || (this.num_bytes_following != expectedSize.toULong())) {
throw IllegalArgumentException("Given data does not look like a kernel cmdline descriptor")
}
this.cmdline = Helper.toCString(Struct("${this.cmdlineLength}s").unpack(data)[0] as ByteArray)
this.cmdline = Struct3("${this.cmdlineLength}s").unpack(data)[0] as String
}
override fun encode(): ByteArray {
val num_bytes_following = SIZE - 16 + cmdline.toByteArray().size
val nbf_with_padding = Helper.round_to_multiple(num_bytes_following.toLong(), 8)
val padding_size = nbf_with_padding - num_bytes_following
val desc = Struct(FORMAT_STRING).pack(
val desc = Struct3(FORMAT_STRING).pack(
TAG,
nbf_with_padding,
this.flags,
cmdline.toByteArray().size)
val padding = Struct("${padding_size}x").pack(null)
cmdline.length)
val padding = Struct3("${padding_size}x").pack(null)
return Helper.join(desc, cmdline.toByteArray(), padding)
}
companion object {
const val TAG = 3L
const val TAG: ULong = 3U
const val SIZE = 24
const val FORMAT_STRING = "!2Q2L" //# tag, num_bytes_following (descriptor header), flags, cmdline length (bytes)
const val flagHashTreeEnabled = 1
const val flagHashTreeDisabled = 2
init {
Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize())
Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize())
}
}
}

@ -1,21 +1,21 @@
package avb.desc
import cfig.Helper
import cfig.io.Struct
import cfig.io.Struct3
import java.io.InputStream
class PropertyDescriptor(
private var key: String = "",
private var value: String = "") : Descriptor(TAG, 0, 0) {
var key: String = "",
var value: String = "") : Descriptor(TAG, 0U, 0) {
override fun encode(): ByteArray {
if (SIZE != Struct(FORMAT_STRING).calcSize()) {
if (SIZE != Struct3(FORMAT_STRING).calcSize()) {
throw RuntimeException()
}
this.num_bytes_following = SIZE + this.key.length + this.value.length + 2 - 16L
val nbfWithPadding = Helper.round_to_multiple(this.num_bytes_following, 8)
this.num_bytes_following = (SIZE + this.key.length + this.value.length + 2 - 16L).toULong()
val nbfWithPadding = Helper.round_to_multiple(this.num_bytes_following.toLong(), 8).toULong()
val paddingSize = nbfWithPadding - num_bytes_following
val padding = Struct("${paddingSize}x").pack(0)
val desc = Struct(FORMAT_STRING).pack(
val padding = Struct3("${paddingSize}x").pack(0)
val desc = Struct3(FORMAT_STRING).pack(
TAG,
nbfWithPadding,
this.key.length,
@ -27,24 +27,24 @@ class PropertyDescriptor(
}
constructor(data: InputStream, seq: Int = 0) : this() {
val info = Struct(FORMAT_STRING).unpack(data)
this.tag = info[0] as Long
this.num_bytes_following = info[1] as Long
val info = Struct3(FORMAT_STRING).unpack(data)
this.tag = info[0] as ULong
this.num_bytes_following = info[1] as ULong
val keySize = info[2] as Long
val valueSize = info[3] as Long
val expectedSize = Helper.round_to_multiple(SIZE - 16 + keySize + 1 + valueSize + 1, 8)
if (this.tag != TAG || expectedSize != this.num_bytes_following) {
if (this.tag != TAG || expectedSize.toULong() != this.num_bytes_following) {
throw IllegalArgumentException("Given data does not look like a |property| descriptor")
}
this.sequence = seq
val info2 = Struct("${keySize}sx${valueSize}s").unpack(data)
val info2 = Struct3("${keySize}sx${valueSize}s").unpack(data)
this.key = String(info2[0] as ByteArray)
this.value = String(info2[2] as ByteArray)
}
companion object {
const val TAG = 0L
const val TAG: ULong = 0U
const val SIZE = 32
const val FORMAT_STRING = "!4Q"
}

@ -1,20 +1,20 @@
package avb.desc
import cfig.Helper
import cfig.io.Struct
import cfig.io.Struct3
import org.apache.commons.codec.binary.Hex
import org.junit.Assert
import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.InputStream
class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0, 0) {
class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0U, 0U, 0) {
@Throws(IllegalArgumentException::class)
constructor(stream: InputStream, seq: Int = 0) : this() {
this.sequence = seq
val info = Struct(FORMAT).unpack(stream)
this.tag = info[0] as Long
this.num_bytes_following = info[1] as Long
val info = Struct3(FORMAT).unpack(stream)
this.tag = info[0] as ULong
this.num_bytes_following = info[1] as ULong
log.debug("UnknownDescriptor: tag = $tag, len = ${this.num_bytes_following}")
this.data = ByteArray(this.num_bytes_following.toInt())
if (this.num_bytes_following.toInt() != stream.read(data)) {
@ -23,7 +23,7 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0,
}
override fun encode(): ByteArray {
return Helper.join(Struct(FORMAT).pack(this.tag, this.data.size.toLong()), data)
return Helper.join(Struct3(FORMAT).pack(this.tag, this.data.size.toLong()), data)
}
override fun toString(): String {
@ -31,20 +31,20 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0,
}
fun analyze(): Any {
return when (this.tag) {
0L -> {
return when (this.tag.toUInt()) {
0U -> {
PropertyDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
}
1L -> {
1U -> {
HashTreeDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
}
2L -> {
2U -> {
HashDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
}
3L -> {
3U -> {
KernelCmdlineDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
}
4L -> {
4U -> {
ChainPartitionDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
}
else -> {
@ -107,7 +107,7 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0,
}
init {
Assert.assertEquals(SIZE, Struct(FORMAT).calcSize())
Assert.assertEquals(SIZE, Struct3(FORMAT).calcSize())
}
}
}

@ -2,8 +2,7 @@ package cfig.bootimg
import cfig.Helper
import cfig.ParamConfig
import cfig.io.Struct
import com.fasterxml.jackson.databind.ObjectMapper
import cfig.io.Struct3
import org.junit.Assert
import org.slf4j.LoggerFactory
import java.io.File
@ -15,27 +14,27 @@ import java.security.MessageDigest
import java.util.regex.Pattern
open class BootImgHeader(
var kernelLength: Long = 0,
var kernelOffset: Long = 0,
var kernelLength: UInt = 0U,
var kernelOffset: UInt = 0U,
var ramdiskLength: Long = 0,
var ramdiskOffset: Long = 0,
var ramdiskLength: UInt = 0U,
var ramdiskOffset: UInt = 0U,
var secondBootloaderLength: Long = 0,
var secondBootloaderOffset: Long = 0,
var secondBootloaderLength: UInt = 0U,
var secondBootloaderOffset: UInt = 0U,
var recoveryDtboLength: Long = 0,
var recoveryDtboOffset: Long = 0,
var recoveryDtboLength: UInt = 0U,
var recoveryDtboOffset: ULong = 0UL,//Q
var dtbLength: Long = 0,
var dtbOffset: Long = 0,
var dtbLength: UInt = 0U,
var dtbOffset: ULong = 0UL,//Q
var tagsOffset: Long = 0,
var tagsOffset: UInt = 0U,
var pageSize: Int = 0,
var pageSize: UInt = 0U,
var headerSize: Long = 0,
var headerVersion: Int = 0,
var headerSize: UInt = 0U,
var headerVersion: UInt = 0U,
var board: String = "",
@ -51,40 +50,40 @@ open class BootImgHeader(
return
}
log.warn("BootImgHeader constructor")
val info = Struct(FORMAT_STRING).unpack(iS)
val info = Struct3(FORMAT_STRING).unpack(iS)
Assert.assertEquals(20, info.size)
if (!(info[0] as ByteArray).contentEquals(magic.toByteArray())) {
if (info[0] != magic) {
throw IllegalArgumentException("stream doesn't look like Android Boot Image Header")
}
this.kernelLength = info[1] as Long
this.kernelOffset = info[2] as Long
this.ramdiskLength = info[3] as Long
this.ramdiskOffset = info[4] as Long
this.secondBootloaderLength = info[5] as Long
this.secondBootloaderOffset = info[6] as Long
this.tagsOffset = info[7] as Long
this.pageSize = (info[8] as Long).toInt()
this.headerVersion = (info[9] as Long).toInt()
val osNPatch = (info[10] as Long).toInt()
if (0 != osNPatch) { //treated as 'reserved' in this boot image
this.osVersion = parseOsVersion(osNPatch shr 11)
this.osPatchLevel = parseOsPatchLevel(osNPatch and 0x7ff)
}
this.board = Helper.toCString(info[11] as ByteArray).trim()
this.cmdline = Helper.toCString(info[12] as ByteArray) + Helper.toCString(info[14] as ByteArray)
this.kernelLength = info[1] as UInt
this.kernelOffset = info[2] as UInt
this.ramdiskLength = info[3] as UInt
this.ramdiskOffset = info[4] as UInt
this.secondBootloaderLength = info[5] as UInt
this.secondBootloaderOffset = info[6] as UInt
this.tagsOffset = info[7] as UInt
this.pageSize = info[8] as UInt
this.headerVersion = info[9] as UInt
val osNPatch = info[10] as UInt
if (0U != osNPatch) { //treated as 'reserved' in this boot image
this.osVersion = parseOsVersion(osNPatch.toInt() shr 11)
this.osPatchLevel = parseOsPatchLevel((osNPatch and 0x7ff.toUInt()).toInt())
}
this.board = info[11] as String
this.cmdline = (info[12] as String) + (info[14] as String)
this.hash = info[13] as ByteArray
if (this.headerVersion > 0) {
this.recoveryDtboLength = info[15] as Long
this.recoveryDtboOffset = info[16] as Long
if (this.headerVersion > 0U) {
this.recoveryDtboLength = info[15] as UInt
this.recoveryDtboOffset = info[16] as ULong
}
this.headerSize = info[17] as Long
this.headerSize = info[17] as UInt
assert(this.headerSize.toInt() in intArrayOf(BOOT_IMAGE_HEADER_V2_SIZE, BOOT_IMAGE_HEADER_V1_SIZE))
if (this.headerVersion > 1) {
this.dtbLength = info[18] as Long
this.dtbOffset = info[19] as Long
if (this.headerVersion > 1U) {
this.dtbLength = info[18] as UInt
this.dtbOffset = info[19] as ULong
}
}
@ -181,45 +180,45 @@ open class BootImgHeader(
private fun refresh() {
val param = ParamConfig()
//refresh kernel size
if (0L == this.kernelLength) {
if (0U == this.kernelLength) {
throw java.lang.IllegalArgumentException("kernel size can not be 0")
} else {
this.kernelLength = File(param.kernel).length()
this.kernelLength = File(param.kernel).length().toUInt()
}
//refresh ramdisk size
if (0L == this.ramdiskLength) {
if (0U == this.ramdiskLength) {
param.ramdisk = null
} else {
this.ramdiskLength = File(param.ramdisk).length()
this.ramdiskLength = File(param.ramdisk).length().toUInt()
}
//refresh second bootloader size
if (0L == this.secondBootloaderLength) {
if (0U == this.secondBootloaderLength) {
param.second = null
} else {
this.secondBootloaderLength = File(param.second).length()
this.secondBootloaderLength = File(param.second).length().toUInt()
}
//refresh recovery dtbo size
if (0L == this.recoveryDtboLength) {
if (0U == this.recoveryDtboLength) {
param.dtbo = null
} else {
this.recoveryDtboLength = File(param.dtbo).length()
this.recoveryDtboLength = File(param.dtbo).length().toUInt()
}
//refresh recovery dtbo size
if (0L == this.dtbLength) {
if (0U == this.dtbLength) {
param.dtb = null
} else {
this.dtbLength = File(param.dtb).length()
this.dtbLength = File(param.dtb).length().toUInt()
}
//refresh image hash
val imageId = when (this.headerVersion) {
0 -> {
0U -> {
hashFileAndSize(param.kernel, param.ramdisk, param.second)
}
1 -> {
1U -> {
hashFileAndSize(param.kernel, param.ramdisk, param.second, param.dtbo)
}
2 -> {
2U -> {
hashFileAndSize(param.kernel, param.ramdisk, param.second, param.dtbo, param.dtb)
}
else -> {
@ -231,8 +230,8 @@ open class BootImgHeader(
fun encode(): ByteArray {
this.refresh()
val ret = Struct(FORMAT_STRING).pack(
"ANDROID!".toByteArray(),
val ret = Struct3(FORMAT_STRING).pack(
"ANDROID!",
//10I
this.kernelLength,
this.kernelOffset,
@ -245,28 +244,28 @@ open class BootImgHeader(
this.headerVersion,
(packOsVersion(this.osVersion) shl 11) or packOsPatchLevel(this.osPatchLevel),
//16s
this.board.toByteArray(),
this.board,
//512s
this.cmdline.substring(0, minOf(512, this.cmdline.length)).toByteArray(),
//32s
this.cmdline.substring(0, minOf(512, this.cmdline.length)),
//32b
this.hash!!,
//1024s
if (this.cmdline.length > 512) this.cmdline.substring(512).toByteArray() else byteArrayOf(0),
if (this.cmdline.length > 512) this.cmdline.substring(512) else "",
//I
this.recoveryDtboLength,
//Q
if (this.headerVersion > 0) this.recoveryDtboOffset else 0,
if (this.headerVersion > 0U) this.recoveryDtboOffset else 0,
//I
when (this.headerVersion) {
0 -> 0
1 -> BOOT_IMAGE_HEADER_V1_SIZE
2 -> BOOT_IMAGE_HEADER_V2_SIZE
0U -> 0
1U -> BOOT_IMAGE_HEADER_V1_SIZE
2U -> BOOT_IMAGE_HEADER_V2_SIZE
else -> java.lang.IllegalArgumentException("headerVersion ${this.headerVersion} illegal")
},
//I
this.dtbLength,
//Q
if (this.headerVersion > 1) this.dtbOffset else 0
if (this.headerVersion > 1U) this.dtbOffset else 0
)
return ret
}
@ -278,7 +277,7 @@ open class BootImgHeader(
"10I" +
"16s" + //board name
"512s" + //cmdline part 1
"32s" + //hash digest
"32b" + //hash digest
"1024s" + //cmdline part 2
"I" + //dtbo length [v1]
"Q" + //dtbo offset [v1]
@ -289,7 +288,7 @@ open class BootImgHeader(
const val BOOT_IMAGE_HEADER_V1_SIZE = 1648
init {
Assert.assertEquals(BOOT_IMAGE_HEADER_V2_SIZE, Struct(FORMAT_STRING).calcSize())
Assert.assertEquals(BOOT_IMAGE_HEADER_V2_SIZE, Struct3(FORMAT_STRING).calcSize())
}
}
}

@ -7,33 +7,33 @@ import java.io.InputStream
class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) {
constructor() : this(null)
val kernelPosition: Int
val kernelPosition: UInt
get() {
return getHeaderSize(this.pageSize)
}
val ramdiskPosition: Int
val ramdiskPosition: UInt
get() {
return (kernelPosition + this.kernelLength +
getPaddingSize(this.kernelLength.toInt(), this.pageSize)).toInt()
getPaddingSize(this.kernelLength, this.pageSize))
}
val secondBootloaderPosition: Int
val secondBootloaderPosition: UInt
get() {
return (ramdiskPosition + ramdiskLength +
getPaddingSize(ramdiskLength.toInt(), pageSize)).toInt()
return ramdiskPosition + ramdiskLength +
getPaddingSize(ramdiskLength, pageSize)
}
val recoveryDtboPosition: Int
val recoveryDtboPosition: ULong
get() {
return (secondBootloaderPosition + secondBootloaderLength +
getPaddingSize(secondBootloaderLength.toInt(), pageSize)).toInt()
return secondBootloaderPosition.toULong() + secondBootloaderLength +
getPaddingSize(secondBootloaderLength, pageSize)
}
val dtbPosition: Int
val dtbPosition: ULong
get() {
return (recoveryDtboPosition + dtbLength +
getPaddingSize(dtbLength.toInt(), pageSize)).toInt()
return recoveryDtboPosition + recoveryDtboLength +
getPaddingSize(recoveryDtboLength, pageSize)
}
var signatureType: BootImgInfo.VerifyType? = null
@ -45,6 +45,15 @@ class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) {
return pad + 1648
}
private fun getHeaderSize(pageSize: UInt): UInt {
val pad = (pageSize - (1648U and (pageSize - 1U))) and (pageSize - 1U)
return pad + 1648U
}
private fun getPaddingSize(position: UInt, pageSize: UInt): UInt {
return (pageSize - (position and pageSize - 1U)) and (pageSize - 1U)
}
private fun getPaddingSize(position: Int, pageSize: Int): Int {
return (pageSize - (position and pageSize - 1)) and (pageSize - 1)
}
@ -59,39 +68,39 @@ class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) {
ret.addArgument(" --kernel ")
ret.addArgument(param.kernel)
ret.addArgument(" --kernel_offset ")
ret.addArgument("0x" + java.lang.Long.toHexString(kernelOffset))
if (this.ramdiskLength > 0) {
ret.addArgument("0x" + Integer.toHexString(kernelOffset.toInt()))
if (this.ramdiskLength > 0U) {
ret.addArgument(" --ramdisk ")
ret.addArgument(param.ramdisk)
}
ret.addArgument(" --ramdisk_offset ")
ret.addArgument("0x" + java.lang.Long.toHexString(ramdiskOffset))
if (this.secondBootloaderLength > 0) {
ret.addArgument("0x" + Integer.toHexString(ramdiskOffset.toInt()))
if (this.secondBootloaderLength > 0U) {
ret.addArgument(" --second ")
ret.addArgument(param.second)
}
ret.addArgument(" --second_offset ")
ret.addArgument("0x" + java.lang.Long.toHexString(this.secondBootloaderOffset))
ret.addArgument("0x" + Integer.toHexString(this.secondBootloaderOffset.toInt()))
if (!board.isBlank()) {
ret.addArgument(" --board ")
ret.addArgument(board)
}
if (headerVersion > 0) {
if (this.recoveryDtboLength > 0) {
if (headerVersion > 0U) {
if (this.recoveryDtboLength > 0U) {
ret.addArgument(" --recovery_dtbo ")
ret.addArgument(param.dtbo)
}
}
if (headerVersion > 1) {
if (this.dtbLength > 0) {
if (headerVersion > 1U) {
if (this.dtbLength > 0U) {
ret.addArgument("--dtb ")
ret.addArgument(param.dtb)
}
ret.addArgument("--dtb_offset ")
ret.addArgument("0x" + java.lang.Long.toHexString(this.dtbOffset))
ret.addArgument("0x" + java.lang.Long.toHexString(this.dtbOffset.toLong()))
}
ret.addArgument(" --pagesize ")
ret.addArgument(Integer.toString(pageSize))
ret.addArgument(Integer.toString(pageSize.toInt()))
ret.addArgument(" --cmdline ")
ret.addArgument(cmdline, false)
if (!osVersion.isNullOrBlank()) {
@ -103,7 +112,7 @@ class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) {
ret.addArgument(osPatchLevel)
}
ret.addArgument(" --tags_offset ")
ret.addArgument("0x" + java.lang.Long.toHexString(tagsOffset))
ret.addArgument("0x" + Integer.toHexString(tagsOffset.toInt()))
ret.addArgument(" --id ")
ret.addArgument(" --output ")
//ret.addArgument("boot.img" + ".google")

@ -0,0 +1,54 @@
package cfig.bootloader_message
import cfig.io.Struct3
import org.junit.Assert
import org.slf4j.LoggerFactory
import java.lang.IllegalStateException
data class BootloaderMsg(
var command: String = "",
var status: String = "",
var recovery: String = "",
var stage: String = "",
var reserved: ByteArray = byteArrayOf()
) {
companion object {
private const val FORMAT_STRING = "32s32s768s32s1184b"
const val SIZE = 2048
private val log = LoggerFactory.getLogger("BootloaderMsg")
init {
Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize())
}
fun writeBootloaderMessage(options: Array<String>) {
val boot = BootloaderMsg()
boot.updateBootloaderMessageInStruct(options)
boot.writeBootloaderMessage()
}
}
fun writeRebootBootloader() {
if (this.command.isNotBlank()) {
throw IllegalStateException("Bootloader command pending.")
}
this.command = "bootonce-bootloader"
writeBootloaderMessage()
}
fun writeBootloaderMessage() {
log.info("writing ... $this")
}
fun updateBootloaderMessageInStruct(options: Array<String>) {
this.command = "boot-recovery"
this.recovery = "recovery"
options.forEach {
this.recovery += if (it.endsWith("\n")) {
it
} else {
it + "\n"
}
}
}
}

@ -0,0 +1,508 @@
package cfig.io
import cfig.Helper
import org.junit.Assert
import org.slf4j.LoggerFactory
import java.io.IOException
import java.io.InputStream
import java.net.URLStreamHandler
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.*
import java.util.regex.Pattern
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?>>()
enum class Type {
Padding,
}
constructor(inFormatString: String) {
formatString = inFormatString
val m = Pattern.compile("(\\d*)([a-zA-Z])").matcher(formatString)
if (formatString.startsWith(">") || formatString.startsWith("!")) {
this.byteOrder = ByteOrder.BIG_ENDIAN
log.debug("Parsing BIG_ENDIAN format: $formatString")
} else if (formatString.startsWith("@") || formatString.startsWith("=")) {
this.byteOrder = ByteOrder.nativeOrder()
log.debug("Parsing native ENDIAN format: $formatString")
} else {
log.debug("Parsing LITTLE_ENDIAN format: $formatString")
}
while (m.find()) {
var bExploded = false
val multiple = if (m.group(1).isEmpty()) 1 else Integer.decode(m.group(1))
//item[0]: Type, item[1]: multiple
// if need to expand format items, explode it
// eg: "4L" will be exploded to "1L 1L 1L 1L"
// eg: "10x" won't be exploded, it's still "10x"
val item = arrayOfNulls<Any?>(2)
when (m.group(2)) {
//exploded types
"x" -> {//byte 1
item[0] = Type.Padding
bExploded = true
}
"b" -> {//byte 1
item[0] = Byte
bExploded = true
}
"B" -> {//UByte 1
item[0] = UByte
bExploded = true
}
"s" -> {//string
item[0] = String
bExploded = true
}
//combo types, which need to be exploded with multiple=1
"c" -> {//char 1
item[0] = Char
bExploded = false
}
"h" -> {//2
item[0] = Short
}
"H" -> {//2
item[0] = UShort
}
"i", "l" -> {//4
item[0] = Int
}
"I", "L" -> {//4
item[0] = UInt
}
"q" -> {//8
item[0] = Long
}
"Q" -> {//8
item[0] = ULong
}
else -> {
throw IllegalArgumentException("type [" + m.group(2) + "] not supported")
}
}
if (bExploded) {
item[1] = multiple
formats.add(item)
} else {
item[1] = 1
for (i in 0 until multiple) {
formats.add(item)
}
}
}
}
private fun getFormatInfo(inCursor: Int): String {
return ("type=" + formats.get(inCursor)[0] + ", value=" + formats.get(inCursor)[1])
}
fun calcSize(): Int? {
var ret = 0
for (format in formats) {
when (format[0]) {
Byte, UByte, Char, String, Type.Padding -> {
ret += format[1] as Int
}
Short, UShort -> {
ret += 2 * format[1] as Int
}
Int, UInt -> {
ret += 4 * format[1] as Int
}
Long, ULong -> {
ret += 8 * format[1] as Int
}
else -> {
throw IllegalArgumentException("Class [" + format[0] + "] not supported")
}
}
}
return ret
}
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)
} else {
log.debug("byte buffer size: " + this.calcSize()!!)
}
val bf = ByteBuffer.allocate(this.calcSize()!!)
bf.order(this.byteOrder)
var formatCursor = -1 //which format item to handle
for (i in args.indices) {
formatCursor++
val arg = args[i]
val format2 = formats[i][0]
val size = formats[i][1] as Int
//x: padding:
// arg == null:
// arg is Byte.class
// arg is Integer.class
if (Type.Padding == format2) {
val b = ByteArray(size)
when (arg) {
null -> Arrays.fill(b, 0.toByte())
is Byte -> Arrays.fill(b, arg)
is Int -> Arrays.fill(b, arg.toByte())
else -> throw IllegalArgumentException("Index[" + i + "] Unsupported arg ["
+ arg + "] with type [" + formats[i][0] + "]")
}
bf.put(b)
continue
}
//c: character
if (Char == format2) {
Assert.assertEquals(1, size.toLong())
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Character.class",
arg is Char)
bf.put(getLowerByte(arg as Char))
continue
}
//b: byte array
if (Byte == format2) {
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of ByteArray/IntArray",
arg is ByteArray || arg is IntArray)
val argInternal = if (arg is IntArray) {
val arg2: MutableList<Byte> = mutableListOf()
for (item in arg) {
Assert.assertTrue("$item is not valid Byte",
item in Byte.MIN_VALUE..Byte.MAX_VALUE)
arg2.add(item.toByte())
}
arg2.toByteArray()
} else {
arg as ByteArray
}
val paddingSize = size - argInternal.size
Assert.assertTrue("argument size overflow: " + argInternal.size + " > " + size,
paddingSize >= 0)
bf.put(argInternal)
if (paddingSize > 0) {
val padBytes = ByteArray(paddingSize)
Arrays.fill(padBytes, 0.toByte())
bf.put(padBytes)
log.debug("paddingSize $paddingSize")
} else {
log.debug("paddingSize is zero, perfect match")
}
continue
}
//B: UByte array
if (UByte == format2) {
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of ByteArray/IntArray",
arg is ByteArray || arg is IntArray || arg is UByteArray)
val argInternal = if (arg is IntArray) {
var arg2: MutableList<Byte> = mutableListOf()
for (item in arg) {
Assert.assertTrue("$item is not valid UByte",
item in UByte.MIN_VALUE.toInt()..UByte.MAX_VALUE.toInt())
arg2.add(item.toByte())
}
arg2.toByteArray()
} else if (arg is UByteArray) {
arg as ByteArray
} else {
arg as ByteArray
}
val paddingSize = size - argInternal.size
Assert.assertTrue("argument size overflow: " + argInternal.size + " > " + size,
paddingSize >= 0)
bf.put(argInternal)
if (paddingSize > 0) {
val padBytes = ByteArray(paddingSize)
Arrays.fill(padBytes, 0.toByte())
bf.put(padBytes)
log.debug("paddingSize $paddingSize")
} else {
log.debug("paddingSize is zero, perfect match")
}
continue
}
//h: Short
if (Short == format2) {
Assert.assertEquals(1, size.toLong())
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Short/Int",
arg is Short || arg is Int)
when (arg) {
is Int -> {
Assert.assertTrue("[$arg] is truncated as type Short.class",
arg in java.lang.Short.MIN_VALUE..java.lang.Short.MAX_VALUE)
bf.putShort(arg.toShort())
}
is Short -> //instance Short
bf.putShort(arg)
}
continue
}
//H: UShort
if (UShort == format2) {
Assert.assertEquals(1, size.toLong())
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of UShort/UInt/Int",
arg is UShort || arg is UInt || arg is Int)
when (arg) {
is Int -> {
Assert.assertFalse("[$arg] is truncated as type UShort",
arg < UShort.MIN_VALUE.toInt() || arg > UShort.MAX_VALUE.toInt())
bf.putShort(arg.toShort())
}
is UInt -> {
Assert.assertFalse("[$arg] is truncated as type UShort",
arg < UShort.MIN_VALUE || arg > UShort.MAX_VALUE)
bf.putShort(arg.toShort())
}
is UShort -> bf.putShort(arg.toShort())
}
continue
}
//i, l: Int
if (Int == format2) {
Assert.assertEquals(1, size.toLong())
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Int", arg is Int)
bf.putInt(arg as Int)
continue
}
//I, L: UInt
if (UInt == format2) {
Assert.assertEquals(1, size.toLong())
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of UInt/Int/Long",
arg is UInt || arg is Int || arg is Long)
when (arg) {
is Int -> {
Assert.assertTrue("[$arg] is invalid as type UInt", arg >= 0)
bf.putInt(arg)
}
is UInt -> bf.putInt(arg.toInt())
is Long -> {
Assert.assertTrue("[$arg] is invalid as type UInt", arg >= 0)
bf.putInt(arg.toInt())
}
else -> {
Assert.fail("program bug")
}
}
continue
}
//q: Long
if (Long == format2) {
Assert.assertEquals(1, size.toLong())
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Long/Int",
arg is Long || arg is Int)
when (arg) {
is Long -> bf.putLong(arg)
is Int -> bf.putLong(arg.toLong())
}
continue
}
//Q: ULong
if (ULong == format2) {
Assert.assertEquals(1, size.toLong())
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Int/Long/ULong",
arg is Int || arg is Long || arg is ULong)
when (arg) {
is Int -> {
Assert.assertTrue("[$arg] is invalid as type ULong", arg >= 0)
bf.putLong(arg.toLong())
}
is Long -> {
Assert.assertTrue("[$arg] is invalid as type ULong", arg >= 0)
bf.putLong(arg)
}
is ULong -> {
bf.putLong(arg.toLong())
}
}
continue
}
//s: String
if (String == format2) {
Assert.assertNotNull("arg can not be NULL for String, formatString=$formatString, ${getFormatInfo(formatCursor)}", arg)
Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of String.class, ${getFormatInfo(formatCursor)}",
arg is String)
val paddingSize = size - (arg as String).length
Assert.assertTrue("argument size overflow: " + arg.length + " > " + size,
paddingSize >= 0)
bf.put(arg.toByteArray())
if (paddingSize > 0) {
val padBytes = ByteArray(paddingSize)
Arrays.fill(padBytes, 0.toByte())
bf.put(padBytes)
log.debug("paddingSize $paddingSize")
} else {
log.debug("paddingSize is zero, perfect match")
}
continue
}
throw java.lang.IllegalArgumentException("unrecognized format $format2")
}
log.debug("Pack Result:" + Helper.toHexString(bf.array()))
return bf.array()
}
@Throws(IOException::class)
fun unpack(iS: InputStream): List<*> {
val ret = ArrayList<Any>()
for (format in this.formats) {
//x: padding
//return padding byte
if (format[0] === Type.Padding) {
val multip = format[1] as Int
val data = ByteArray(1)
iS.read(data)//sample the 1st byte
val skipped = iS.skip(multip.toLong() - 1)//skip remaining
Assert.assertEquals(multip.toLong() - 1, skipped)
ret.add(data[0])
continue
}
//b: byte array
if (format[0] === Byte) {
val data = ByteArray(format[1] as Int)
Assert.assertEquals(format[1] as Int, iS.read(data))
ret.add(data)
continue
}
//B: ubyte array
if (format[0] === UByte) {
val data = ByteArray(format[1] as Int)
Assert.assertEquals(format[1] as Int, iS.read(data))
val innerData = UByteArray(data.size)
for (i in 0 until data.size) {
innerData[i] = data[i].toUByte()
}
ret.add(innerData)
continue
}
//char: 1
if (format[0] === Char) {
val data = ByteArray(format[1] as Int)//now its size is fixed at 1
Assert.assertEquals(format[1] as Int, iS.read(data))
ret.add(data[0].toChar())
continue
}
//string
if (format[0] === String) {
val data = ByteArray(format[1] as Int)
Assert.assertEquals(format[1] as Int, iS.read(data))
ret.add(Helper.toCString(data))
continue
}
//h: short
if (format[0] === Short) {
val data = ByteArray(2)
Assert.assertEquals(2, iS.read(data).toLong())
ByteBuffer.allocate(2).let {
it.order(this.byteOrder)
it.put(data)
it.flip()
ret.add(it.short)
}
continue
}
//H: UShort
if (format[0] === UShort) {
val data = ByteArray(2)
Assert.assertEquals(2, iS.read(data).toLong())
ByteBuffer.allocate(2).let {
it.order(this.byteOrder)
it.put(data)
it.flip()
ret.add(it.short.toUShort())
}
continue
}
//i, l: Int
if (format[0] === Int) {
val data = ByteArray(4)
Assert.assertEquals(4, iS.read(data).toLong())
ByteBuffer.allocate(4).let {
it.order(this.byteOrder)
it.put(data)
it.flip()
ret.add(it.int)
}
continue
}
//I, L: UInt
if (format[0] === UInt) {
val data = ByteArray(4)
Assert.assertEquals(4, iS.read(data).toLong())
ByteBuffer.allocate(4).let {
it.order(this.byteOrder)
it.put(data)
it.flip()
ret.add(it.int.toUInt())
}
continue
}
//q: Long
if (format[0] === Long) {
val data = ByteArray(8)
Assert.assertEquals(8, iS.read(data).toLong())
ByteBuffer.allocate(8).let {
it.order(this.byteOrder)
it.put(data)
it.flip()
ret.add(it.long)
}
continue
}
//Q: ULong
if (format[0] === ULong) {
val data = ByteArray(8)
Assert.assertEquals(8, iS.read(data).toLong())
ByteBuffer.allocate(8).let {
it.order(this.byteOrder)
it.put(data)
it.flip()
ret.add(it.long.toULong())
}
continue
}
throw IllegalArgumentException("Class [" + format[0] + "] not supported")
}
return ret
}
//get lower 1 byte
private fun getLowerByte(obj: Char?): Byte {
val bf2 = ByteBuffer.allocate(Character.SIZE / 8) //aka. 16/8
bf2.putChar(obj!!)
bf2.flip()
bf2.get()
return bf2.get()
}
}

@ -0,0 +1,113 @@
package cfig.init
import cfig.bootloader_message.BootloaderMsg
import org.slf4j.LoggerFactory
import java.util.*
class Reboot {
enum class RB_TYPE {
ANDROID_RB_RESTART,
ANDROID_RB_POWEROFF,
ANDROID_RB_RESTART2,
ANDROID_RB_THERMOFF
}
companion object {
private val log = LoggerFactory.getLogger(Reboot::class.java)
const val dynamicPartitionKey = "ro.boot.dynamic_partitions"
const val lastRebootReasonKey = "persist.sys.boot.reason"
private fun doReboot(cmd: RB_TYPE, reason: String, rebootTarget: String) {
val reasons = reason.split(",").toTypedArray()
val props = Properties()
props.setProperty(lastRebootReasonKey, reason)
if (reasons.size > 1 && reasons[0] == "reboot") {
if (reasons[1] in setOf("recovery", "bootloader", "cold", "hard", "warn")) {
props.setProperty(lastRebootReasonKey, reason.substring(7))
} else {
//pass
}
}
}
private fun doReboot(cmd: String, rebootTarget: String) {
log.info("cmd=[$cmd], rebootTarget=[$rebootTarget]")
}
// setprop sys.powerctl <value>
fun handlePowerctlMessage(inValue: String, props: Properties? = null) {
log.info("handlePowerctlMessage($inValue)")
val args = inValue.split(",").toTypedArray()
var cmd: String
var rebootTarget = ""
if (args.size > 3) {
throw java.lang.IllegalArgumentException("powerctl: unrecognized command $args")
}
when (args[0]) {
"shutdown" -> {
cmd = "ANDROID_RB_POWEROFF"
if (args.size == 2) {
when (args[1]) {
"userrequested" -> {
log.info("need to run_fsck")
}
"thermal" -> {
cmd = "ANDROID_RB_THERMOFF"
log.info("TurnOffBacklight()")
}
else -> {
//pass
}
}
} else {
//pass
}
}
"reboot" -> {
cmd = "ANDROID_RB_RESTART2"
if (args.size >= 2) {
rebootTarget = args[1]
val bDynamicPartition: Boolean? = props?.let { propsNotNull ->
propsNotNull.get(dynamicPartitionKey)?.let { if (it == "true") true else null }
}
when (rebootTarget) {
"fastboot" -> {
if (bDynamicPartition == null || bDynamicPartition == false) {
log.warn("$dynamicPartitionKey=false, using 'bootloader fastboot' instead of 'fastbootd'")
rebootTarget = "bootloader"
BootloaderMsg().writeRebootBootloader()
} else {
log.info("$dynamicPartitionKey=true, using fastbootd")
BootloaderMsg.writeBootloaderMessage(arrayOf("--fastboot"))
rebootTarget = "recovery"
}
}
"bootloader" -> {
BootloaderMsg().writeRebootBootloader()
}
"sideload", "sideload-auto-reboot" -> {
BootloaderMsg.writeBootloaderMessage(
arrayOf("--" + rebootTarget.replace("-", "_")))
rebootTarget = "recovery"
}
else -> {
}
}//end-of-when-rebootTarget
if (args.size == 3 && args[2].isNotBlank()) {
log.info("rebootTarget: append " + args[2])
rebootTarget += ("," + args[2])
}
}//end-of-cmd
}//end-of-cmd=reboot
else -> {//not shutdown/reboot
throw java.lang.IllegalArgumentException("powerctl: unrecognized command $args")
}
}//end-of-args[0]
log.debug("ActionManager::GetInstance().ClearQueue()")
log.debug("ActionManager::GetInstance().QueueEventTrigger(\"shutdown\")")
log.debug("ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, \"shutdown_done\")")
doReboot(cmd, rebootTarget)
}//end-of-handlePowerctlMessage
}//end-of-companion
}

@ -15,11 +15,11 @@ class FooterTest {
it.skip(footerBytes.size - 64L)
val footer = Footer(it)
println(footer.toString())
assertEquals(1, footer.versionMajor)
assertEquals(0, footer.versionMinor)
assertEquals(512, footer.vbMetaSize)
assertEquals(28983296, footer.vbMetaOffset)
assertEquals(28983296, footer.originalImageSize)
assertEquals(1U, footer.versionMajor)
assertEquals(0U, footer.versionMinor)
assertEquals(512U.toULong(), footer.vbMetaSize)
assertEquals(28983296U.toULong(), footer.vbMetaOffset)
assertEquals(28983296U.toULong(), footer.originalImageSize)
}
}

@ -8,7 +8,7 @@ class HeaderTest {
@Test
fun readHeader() {
val vbmetaHeaderStr = "4156423000000001000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000c80000000000000000000000000000000000000000000000c800000000000000000000000000000000617662746f6f6c20312e312e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
val vbmetaHeaderStr = "4156423000000001000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000c80000000000000000000000000000000000000000000000c800000000000000000000000000000000617662746f6f6c20312e312e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
val header2 = Header(ByteArrayInputStream(Hex.decodeHex(vbmetaHeaderStr)))
println(header2.toString())
}

@ -1,6 +1,5 @@
package avb.alg
import cfig.io.Struct
import org.junit.Test
import org.junit.Assert.*

@ -7,7 +7,7 @@ import org.junit.Test
class AlgorithmsTest {
@Test
fun test() {
fun test1() {
val alg = Algorithms.get("NONE")!!
Assert.assertEquals(Helper.toHexString(Algorithms.get("SHA256_RSA4096")!!.padding),

@ -0,0 +1,433 @@
import cfig.Helper
import cfig.io.Struct3
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.Assert
import org.junit.Assert.*
import org.junit.Test
import java.io.ByteArrayInputStream
class Struct3Test {
private fun getConvertedFormats(inStruct: Struct3): ArrayList<Map<String, Int>> {
val f = inStruct.javaClass.getDeclaredField("formats")
f.isAccessible = true
val formatDumps = arrayListOf<Map<String, Int>>()
(f.get(inStruct) as ArrayList<*>).apply {
this.forEach {
val format = it as Array<Object>
val k = if (format[0].toString().indexOf(" ") > 0) {
format[0].toString().split(" ")[1]
} else {
format[0].toString()
}
formatDumps.add(mapOf(k to (format[1] as Int)))
}
}
return formatDumps
}
private fun constructorTestFun1(inFormatString: String) {
println(ObjectMapper().writeValueAsString(getConvertedFormats(Struct3(inFormatString))))
}
@Test
fun constructorTest() {
constructorTestFun1("3s")
constructorTestFun1("5b")
constructorTestFun1("5x")
constructorTestFun1("2c")
}
@Test
fun calcSizeTest() {
Assert.assertEquals(3, Struct3("3s").calcSize())
Assert.assertEquals(5, Struct3("5b").calcSize())
Assert.assertEquals(5, Struct3("5x").calcSize())
Assert.assertEquals(9, Struct3("9c").calcSize())
}
//x
@Test
fun paddingTest() {
Assert.assertEquals("0000000000", Helper.toHexString(Struct3("5x").pack(null)))
Assert.assertEquals("0000000000", Helper.toHexString(Struct3("5x").pack(0)))
Assert.assertEquals("0101010101", Helper.toHexString(Struct3("5x").pack(1)))
Assert.assertEquals("1212121212", Helper.toHexString(Struct3("5x").pack(0x12)))
//Integer高位被截掉
Assert.assertEquals("2323232323", Helper.toHexString(Struct3("5x").pack(0x123)))
// minus 0001_0011 -> 补码 1110 1101ie. 0xed
Assert.assertEquals("ededededed", Helper.toHexString(Struct3("5x").pack(-0x13)))
//0xff
Assert.assertEquals("ffffffffff", Helper.toHexString(Struct3("5x").pack(-1)))
try {
Struct3("5x").pack("bad")
Assert.assertTrue("should throw exception here", false)
} catch (e: IllegalArgumentException) {
}
//unpack
Struct3("3x").unpack(ByteArrayInputStream(Helper.fromHexString("000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(0.toByte(), it[0])
}
Struct3("x2xx").unpack(ByteArrayInputStream(Helper.fromHexString("01121210"))).let {
Assert.assertEquals(3, it.size)
Assert.assertEquals(0x1.toByte(), it[0])
Assert.assertEquals(0x12.toByte(), it[1])
Assert.assertEquals(0x10.toByte(), it[2])
}
}
//c
@Test
fun characterTest() {
//constructor
Struct3("c")
//calcSize
Assert.assertEquals(3, Struct3("3c").calcSize())
//pack illegal
try {
Struct3("c").pack("a")
Assert.fail("should throw exception here")
} catch (e: Throwable) {
Assert.assertTrue(e is AssertionError || e is IllegalArgumentException)
}
//pack legal
Assert.assertEquals("61",
Helper.toHexString(Struct3("!c").pack(Character('a'))))
Assert.assertEquals("61",
Helper.toHexString(Struct3("c").pack(Character('a'))))
Assert.assertEquals("616263",
Helper.toHexString(Struct3("3c").pack('a', 'b', 'c')))
//unpack
Struct3("3c").unpack(ByteArrayInputStream(Helper.fromHexString("616263"))).let {
Assert.assertEquals(3, it.size)
Assert.assertEquals('a', it[0])
Assert.assertEquals('b', it[1])
Assert.assertEquals('c', it[2])
}
}
//b
@Test
fun bytesTest() {
//constructor
Struct3("b")
//calcSize
Assert.assertEquals(3, Struct3("3b").calcSize())
//pack
Assert.assertEquals("123456", Helper.toHexString(
Struct3("3b").pack(byteArrayOf(0x12, 0x34, 0x56))))
Assert.assertEquals("123456", Helper.toHexString(
Struct3("!3b").pack(byteArrayOf(0x12, 0x34, 0x56))))
Assert.assertEquals("123400", Helper.toHexString(
Struct3("3b").pack(byteArrayOf(0x12, 0x34))))
//unpack
Struct3("3b").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals("123400", Helper.toHexString(it[0] as ByteArray))
}
Struct3("bbb").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let {
Assert.assertEquals(3, it.size)
Assert.assertEquals("12", Helper.toHexString(it[0] as ByteArray))
Assert.assertEquals("34", Helper.toHexString(it[1] as ByteArray))
Assert.assertEquals("00", Helper.toHexString(it[2] as ByteArray))
}
}
//B: UByte array
@Test
fun uBytesTest() {
//constructor
Struct3("B")
//calcSize
Assert.assertEquals(3, Struct3("3B").calcSize())
//pack
Assert.assertEquals("123456", Helper.toHexString(
Struct3("3B").pack(byteArrayOf(0x12, 0x34, 0x56))))
Assert.assertEquals("123456", Helper.toHexString(
Struct3("!3B").pack(byteArrayOf(0x12, 0x34, 0x56))))
Assert.assertEquals("123400", Helper.toHexString(
Struct3("3B").pack(byteArrayOf(0x12, 0x34))))
//unpack
Struct3("3B").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals("123400", Helper.toHexString(it[0] as UByteArray))
}
Struct3("BBB").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let {
Assert.assertEquals(3, it.size)
Assert.assertEquals("12", Helper.toHexString(it[0] as UByteArray))
Assert.assertEquals("34", Helper.toHexString(it[1] as UByteArray))
Assert.assertEquals("00", Helper.toHexString(it[2] as UByteArray))
}
}
//s
@Test
fun stringTest() {
//constructor
Struct3("s")
//calcSize
Assert.assertEquals(3, Struct3("3s").calcSize())
//pack
Struct3("3s").pack("a")
Struct3("3s").pack("abc")
try {
Struct3("3s").pack("abcd")
Assert.fail("should throw exception here")
} catch (e: Throwable) {
Assert.assertTrue(e is AssertionError || e is IllegalArgumentException)
}
//unpack
Struct3("3s").unpack(ByteArrayInputStream(Helper.fromHexString("616263"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals("abc", it[0])
}
Struct3("3s").unpack(ByteArrayInputStream(Helper.fromHexString("610000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals("a", it[0])
}
}
//h
@Test
fun shortTest() {
//constructor
Struct3("h")
//calcSize
Assert.assertEquals(6, Struct3("3h").calcSize())
//pack
Assert.assertEquals("ff7f", Helper.toHexString(Struct3("h").pack(0x7fff)))
Assert.assertEquals("0080", Helper.toHexString(Struct3("h").pack(-0x8000)))
Assert.assertEquals("7fff0000", Helper.toHexString(Struct3(">2h").pack(0x7fff, 0)))
//unpack
Struct3(">2h").unpack(ByteArrayInputStream(Helper.fromHexString("7fff0000"))).let {
Assert.assertEquals(2, it.size)
Assert.assertEquals(0x7fff.toShort(), it[0])
Assert.assertEquals(0.toShort(), it[1])
}
}
//H
@Test
fun uShortTest() {
//constructor
Struct3("H")
//calcSize
Assert.assertEquals(6, Struct3("3H").calcSize())
//pack
Assert.assertEquals("0100", Helper.toHexString(Struct3("H").pack((1U).toUShort())))
Assert.assertEquals("0100", Helper.toHexString(Struct3("H").pack(1U)))
Assert.assertEquals("ffff", Helper.toHexString(Struct3("H").pack(65535U)))
Assert.assertEquals("ffff", Helper.toHexString(Struct3("H").pack(65535)))
try {
Struct3("H").pack(-1)
Assert.fail("should throw exception here")
} catch (e: Throwable) {
Assert.assertTrue(e is AssertionError || e is IllegalArgumentException)
}
//unpack
Struct3("H").unpack(ByteArrayInputStream(Helper.fromHexString("ffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(65535U.toUShort(), it[0])
}
}
//i, l
@Test
fun intTest() {
//constructor
Struct3("i")
Struct3("l")
//calcSize
Assert.assertEquals(12, Struct3("3i").calcSize())
Assert.assertEquals(12, Struct3("3l").calcSize())
//pack
Struct3("i").pack(65535 + 1)
Struct3("i").pack(-1)
//unpack
Struct3("i").unpack(ByteArrayInputStream(Helper.fromHexString("00000100"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(65536, it[0])
}
Struct3("i").unpack(ByteArrayInputStream(Helper.fromHexString("ffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(-1, it[0])
}
}
//I, L
@Test
fun uIntTest() {
//constructor
Struct3("I")
Struct3("L")
//calcSize
Assert.assertEquals(12, Struct3("3I").calcSize())
Assert.assertEquals(12, Struct3("3L").calcSize())
//pack
Assert.assertEquals("01000000", Helper.toHexString(
Struct3("I").pack(1U)))
Assert.assertEquals("80000000", Helper.toHexString(
Struct3(">I").pack(Int.MAX_VALUE.toUInt() + 1U)))
//unpack
Struct3("I").unpack(ByteArrayInputStream(Helper.fromHexString("01000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(1U, it[0])
}
Struct3(">I").unpack(ByteArrayInputStream(Helper.fromHexString("80000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(Int.MAX_VALUE.toUInt() + 1U, it[0])
}
}
//q: Long
@Test
fun longTest() {
//constructor
Struct3("q")
//calcSize
Assert.assertEquals(24, Struct3("3q").calcSize())
//pack
Assert.assertEquals("8000000000000000", Helper.toHexString(
Struct3(">q").pack(Long.MIN_VALUE)))
Assert.assertEquals("7fffffffffffffff", Helper.toHexString(
Struct3(">q").pack(Long.MAX_VALUE)))
Assert.assertEquals("ffffffffffffffff", Helper.toHexString(
Struct3(">q").pack(-1L)))
//unpack
Struct3(">q").unpack(ByteArrayInputStream(Helper.fromHexString("8000000000000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(Long.MIN_VALUE, it[0])
}
Struct3(">q").unpack(ByteArrayInputStream(Helper.fromHexString("7fffffffffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(Long.MAX_VALUE, it[0])
}
Struct3(">q").unpack(ByteArrayInputStream(Helper.fromHexString("ffffffffffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(-1L, it[0])
}
}
//Q: ULong
@Test
fun uLongTest() {
//constructor
Struct3("Q")
//calcSize
Assert.assertEquals(24, Struct3("3Q").calcSize())
//pack
Assert.assertEquals("7fffffffffffffff", Helper.toHexString(
Struct3(">Q").pack(Long.MAX_VALUE)))
Assert.assertEquals("0000000000000000", Helper.toHexString(
Struct3(">Q").pack(ULong.MIN_VALUE)))
Assert.assertEquals("ffffffffffffffff", Helper.toHexString(
Struct3(">Q").pack(ULong.MAX_VALUE)))
try {
Struct3(">Q").pack(-1L)
} catch (e: Throwable) {
Assert.assertTrue(e is AssertionError || e is IllegalArgumentException)
}
//unpack
Struct3(">Q").unpack(ByteArrayInputStream(Helper.fromHexString("7fffffffffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(Long.MAX_VALUE.toULong(), it[0])
}
Struct3(">Q").unpack(ByteArrayInputStream(Helper.fromHexString("0000000000000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(ULong.MIN_VALUE, it[0])
}
Struct3(">Q").unpack(ByteArrayInputStream(Helper.fromHexString("ffffffffffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(ULong.MAX_VALUE, it[0])
}
}
@Test
fun legacyTest() {
Assert.assertTrue(Struct3("<2i4b4b").pack(
1, 7321, byteArrayOf(1, 2, 3, 4), byteArrayOf(200.toByte(), 201.toByte(), 202.toByte(), 203.toByte()))!!
.contentEquals(Helper.fromHexString("01000000991c000001020304c8c9cacb")))
Assert.assertTrue(Struct3("<2i4b4B").pack(
1, 7321, byteArrayOf(1, 2, 3, 4), intArrayOf(200, 201, 202, 203))!!
.contentEquals(Helper.fromHexString("01000000991c000001020304c8c9cacb")))
Assert.assertTrue(Struct3("b2x").pack(byteArrayOf(0x13), null).contentEquals(Helper.fromHexString("130000")))
Assert.assertTrue(Struct3("b2xi").pack(byteArrayOf(0x13), null, 55).contentEquals(Helper.fromHexString("13000037000000")))
Struct3("5s").pack("Good").contentEquals(Helper.fromHexString("476f6f6400"))
Struct3("5s1b").pack("Good", byteArrayOf(13)).contentEquals(Helper.fromHexString("476f6f64000d"))
}
@Test
fun legacyIntegerLE() {
//int (4B)
assertTrue(Struct3("<2i").pack(1, 7321).contentEquals(Helper.fromHexString("01000000991c0000")))
val ret = Struct3("<2i").unpack(ByteArrayInputStream(Helper.fromHexString("01000000991c0000")))
assertEquals(2, ret.size)
assertTrue(ret[0] is Int)
assertTrue(ret[1] is Int)
assertEquals(1, ret[0] as Int)
assertEquals(7321, ret[1] as Int)
//unsigned int (4B)
assertTrue(Struct3("<I").pack(2L).contentEquals(Helper.fromHexString("02000000")))
assertTrue(Struct3("<I").pack(2).contentEquals(Helper.fromHexString("02000000")))
//greater than Int.MAX_VALUE
assertTrue(Struct3("<I").pack(2147483748L).contentEquals(Helper.fromHexString("64000080")))
assertTrue(Struct3("<I").pack(2147483748).contentEquals(Helper.fromHexString("64000080")))
try {
Struct3("<I").pack(-12)
throw Exception("should not reach here")
} catch (e: Throwable) {
Assert.assertTrue(e is AssertionError || e is IllegalArgumentException)
}
//negative int
assertTrue(Struct3("<i").pack(-333).contentEquals(Helper.fromHexString("b3feffff")))
}
@Test
fun legacyIntegerBE() {
run {
assertTrue(Struct3(">2i").pack(1, 7321).contentEquals(Helper.fromHexString("0000000100001c99")))
val ret = Struct3(">2i").unpack(ByteArrayInputStream(Helper.fromHexString("0000000100001c99")))
assertEquals(1, ret[0] as Int)
assertEquals(7321, ret[1] as Int)
}
run {
assertTrue(Struct3("!i").pack(-333).contentEquals(Helper.fromHexString("fffffeb3")))
val ret2 = Struct3("!i").unpack(ByteArrayInputStream(Helper.fromHexString("fffffeb3")))
assertEquals(-333, ret2[0] as Int)
}
}
}

@ -1,13 +1,39 @@
import cfig.Helper
import cfig.io.Struct
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.Test
import org.junit.Assert.*
import java.io.ByteArrayInputStream
import kotlin.reflect.jvm.jvmName
class StructTest {
private fun getConvertedFormats(inStruct: Struct): ArrayList<Map<String, Int>> {
val f = inStruct.javaClass.getDeclaredField("formats")
f.isAccessible = true
val formatDumps = arrayListOf<Map<String, Int>>()
(f.get(inStruct) as ArrayList<*>).apply {
this.forEach {
val format = it as Array<Object>
formatDumps.add(mapOf(format[0].toString().split(" ")[1] to (format[1] as Int)))
}
}
return formatDumps
}
private fun constructorTestFun1(inFormatString: String) {
println(ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(getConvertedFormats(Struct(inFormatString))))
}
@Test
fun constructorTest() {
constructorTestFun1("2s")
constructorTestFun1("2b")
constructorTestFun1("2bs")
}
@Test
fun constructTest() {
fun calcSizeTest() {
assertEquals(16, Struct("<2i4b4b").calcSize())
assertEquals(16, Struct("<Q8b").calcSize())
assertEquals(2, Struct(">h").calcSize())

@ -0,0 +1,39 @@
package init
import cfig.init.Reboot
import org.junit.Assert.*
import org.junit.Test
import java.util.*
class RebootTest {
@Test
fun testDifferentModes() {
Reboot.handlePowerctlMessage("reboot,recovery")
Reboot.handlePowerctlMessage("reboot")
Reboot.handlePowerctlMessage("reboot,safemode")
Reboot.handlePowerctlMessage("reboot,dynsystem")
Reboot.handlePowerctlMessage("reboot,quiescent")
}
@Test
fun shutdown() {
Reboot.handlePowerctlMessage("shutdown,userrequested")
Reboot.handlePowerctlMessage("shutdown,thermal")
Reboot.handlePowerctlMessage("shutdown,thermal,battery")
}
@Test
fun fastbootd() {
Reboot.handlePowerctlMessage("reboot,bootloader")
val props = Properties()
Reboot.handlePowerctlMessage("reboot,fastboot", props)
props.put(Reboot.dynamicPartitionKey, "true")
Reboot.handlePowerctlMessage("reboot,fastboot", props)
}
@Test
fun sideload() {
Reboot.handlePowerctlMessage("reboot,sideload-auto-reboot")
Reboot.handlePowerctlMessage("reboot,sideload")
}
}

@ -0,0 +1,20 @@
# /misc partition layout
| - | - | - | size | description |
| :---- | :------ | :---- | :---- | :---- |
| bootloader_message_ab | | | 4096 | |
| | bootloader_message | | 2048 | |
| | |command | 32 |updated by linux/bootloader |
| | |status | 32 |deprecated |
| | |recovery |768 |talking channel between normal/recovery modes |
| | |stage | 32 |format "#/#", eg, "1/3" |
| | |reserved | 1184| |
| |slot_suffix | | 32| |
| |update_channel | |128 | |
| |reserved | | 1888| |
| | | | | |
| vendor bootloader msg | | | 12 KB | offset 4kB |
| wipe_package info | | | 48K | offset 16KB, Used by uncrypt and recovery to store wipe_package for A/B devices |
|- |- |- |- |- |
| | | | | |

@ -3,6 +3,12 @@
import shutil, os.path, json, subprocess, hashlib, glob
import unittest
successLogo = """
+----------------------------------+
| All test cases have PASSED |
+----------------------------------+
"""
def hashFile(fileName):
hasher = hashlib.md5()
with open(fileName, 'rb') as afile:
@ -69,3 +75,5 @@ verifySingleJson(resDir, "9.0.0_blueline_pq1a.181105.017.a1", "%s/9.0.0_blueline
# from volunteers
verifySingleDir(resDir, "recovery_image_from_s-trace")
print(successLogo)

Loading…
Cancel
Save