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 { buildscript {
ext { ext {
kotlinVersion = "1.3.20" kotlinVersion = "1.3.30"
} }
repositories { repositories {
mavenCentral() mavenCentral()

@ -28,6 +28,9 @@ public class Struct {
if (formatString.startsWith(">") || formatString.startsWith("!")) { if (formatString.startsWith(">") || formatString.startsWith("!")) {
this.byteOrder = ByteOrder.BIG_ENDIAN; this.byteOrder = ByteOrder.BIG_ENDIAN;
log.debug("Parsing BIG_ENDIAN format: " + formatString); 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 { } else {
log.debug("Parsing LITTLE_ENDIAN format: " + formatString); log.debug("Parsing LITTLE_ENDIAN format: " + formatString);
} }
@ -38,7 +41,12 @@ public class Struct {
if (!m.group(1).isEmpty()) { if (!m.group(1).isEmpty()) {
mul = Integer.decode(m.group(1)); 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]; Object item[] = new Object[2];
switch (m.group(2)) { switch (m.group(2)) {
case "x": {//byte 1 case "x": {//byte 1
item[0] = PadByte.class; item[0] = PadByte.class;
@ -98,7 +106,7 @@ public class Struct {
} }
public Integer calcSize() { public Integer calcSize() {
Integer ret = 0; int ret = 0;
for (Object[] format : formats) { for (Object[] format : formats) {
if (format[0] == Byte.class || format[0] == Character.class || format[0] == PadByte.class) { if (format[0] == Byte.class || format[0] == Character.class || format[0] == PadByte.class) {
ret += (int) format[1]; ret += (int) format[1];
@ -239,7 +247,7 @@ public class Struct {
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
Object arg = args[i]; Object arg = args[i];
Class<?> format = (Class<?>) formats.get(i)[0]; 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); log.debug("Index[" + i + "], fmt = " + format + ", arg = " + arg + ", multi = " + size);
//padding //padding

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

@ -1,7 +1,8 @@
package cfig package cfig
import cfig.io.Struct import cfig.io.Struct3
import com.google.common.math.BigIntegerMath 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.codec.binary.Hex
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
import org.apache.commons.compress.compressors.gzip.GzipParameters import org.apache.commons.compress.compressors.gzip.GzipParameters
@ -42,6 +43,14 @@ class Helper {
return baos.toByteArray() 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 { fun toHexString(inData: ByteArray): String {
val sb = StringBuilder() val sb = StringBuilder()
for (i in inData.indices) { 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 { fun round_to_multiple(size: Long, page: Int): Long {
val remainder = size % page val remainder = size % page
return if (remainder == 0L) { 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) 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 val unsignedModulo = rsa.modulus.toByteArray().sliceArray(1..numBits / 8) //remove sign byte
log.debug("unsigned modulo: " + Hex.encodeHexString(unsignedModulo)) 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, numBits,
n0inv, n0inv,
unsignedModulo, unsignedModulo,

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

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

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

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

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

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

@ -2,7 +2,7 @@ package avb
import avb.alg.Algorithms import avb.alg.Algorithms
import cfig.Helper import cfig.Helper
import cfig.io.Struct import cfig.io.Struct3
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.security.MessageDigest import java.security.MessageDigest
@ -15,7 +15,7 @@ class Blob {
val auxSize = Helper.round_to_multiple( val auxSize = Helper.round_to_multiple(
(encodedDesc.size + encodedKey.size + pkmdBlob.size).toLong(), (encodedDesc.size + encodedKey.size + pkmdBlob.size).toLong(),
64) 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, 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)) binarySignature = Helper.rawSign(alg.defaultKey.replace(".pem", ".pk8"), Helper.join(alg.padding, binaryHash))
} }
val authData = Helper.join(binaryHash, binarySignature) 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 package avb
import cfig.io.Struct import cfig.io.Struct3
import org.junit.Assert import org.junit.Assert
import java.io.InputStream 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( data class Footer constructor(
var versionMajor: Long = FOOTER_VERSION_MAJOR, var versionMajor: UInt = FOOTER_VERSION_MAJOR,
var versionMinor: Long = FOOTER_VERSION_MINOR, var versionMinor: UInt = FOOTER_VERSION_MINOR,
var originalImageSize: Long = 0L, var originalImageSize: ULong = 0U,
var vbMetaOffset: Long = 0L, var vbMetaOffset: ULong = 0U,
var vbMetaSize: Long = 0L var vbMetaSize: ULong = 0U
) { ) {
companion object { companion object {
const val MAGIC = "AVBf" const val MAGIC = "AVBf"
const val SIZE = 64 const val SIZE = 64
private const val RESERVED = 28 private const val RESERVED = 28
const val FOOTER_VERSION_MAJOR = 1L const val FOOTER_VERSION_MAJOR = 1U
const val FOOTER_VERSION_MINOR = 0L const val FOOTER_VERSION_MINOR = 0U
private const val FORMAT_STRING = "!4s2L3Q${RESERVED}x" private const val FORMAT_STRING = "!4s2L3Q${RESERVED}x"
init { init {
Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize()) Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize())
} }
} }
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
constructor(iS: InputStream) : this() { constructor(iS: InputStream) : this() {
val info = Struct(FORMAT_STRING).unpack(iS) val info = Struct3(FORMAT_STRING).unpack(iS)
Assert.assertEquals(7, info.size) 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") throw IllegalArgumentException("stream doesn't look like valid AVB Footer")
} }
versionMajor = info[1] as Long versionMajor = info[1] as UInt
versionMinor = info[2] as Long versionMinor = info[2] as UInt
originalImageSize = info[3] as Long originalImageSize = info[3] as ULong
vbMetaOffset = info[4] as Long vbMetaOffset = info[4] as ULong
vbMetaSize = info[5] as Long vbMetaSize = info[5] as ULong
} }
fun encode(): ByteArray { fun encode(): ByteArray {
return Struct(FORMAT_STRING).pack(Footer.MAGIC.toByteArray(), return Struct3(FORMAT_STRING).pack(MAGIC,
this.versionMajor, this.versionMajor,
this.versionMinor, this.versionMinor,
this.originalImageSize, this.originalImageSize,

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

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

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

@ -1,5 +1,5 @@
package avb.desc 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 abstract fun encode(): ByteArray
} }

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

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

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

@ -1,20 +1,20 @@
package avb.desc package avb.desc
import cfig.Helper import cfig.Helper
import cfig.io.Struct import cfig.io.Struct3
import org.apache.commons.codec.binary.Hex import org.apache.commons.codec.binary.Hex
import org.junit.Assert import org.junit.Assert
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.InputStream 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) @Throws(IllegalArgumentException::class)
constructor(stream: InputStream, seq: Int = 0) : this() { constructor(stream: InputStream, seq: Int = 0) : this() {
this.sequence = seq this.sequence = seq
val info = Struct(FORMAT).unpack(stream) val info = Struct3(FORMAT).unpack(stream)
this.tag = info[0] as Long this.tag = info[0] as ULong
this.num_bytes_following = info[1] as Long this.num_bytes_following = info[1] as ULong
log.debug("UnknownDescriptor: tag = $tag, len = ${this.num_bytes_following}") log.debug("UnknownDescriptor: tag = $tag, len = ${this.num_bytes_following}")
this.data = ByteArray(this.num_bytes_following.toInt()) this.data = ByteArray(this.num_bytes_following.toInt())
if (this.num_bytes_following.toInt() != stream.read(data)) { 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 { 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 { override fun toString(): String {
@ -31,20 +31,20 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0,
} }
fun analyze(): Any { fun analyze(): Any {
return when (this.tag) { return when (this.tag.toUInt()) {
0L -> { 0U -> {
PropertyDescriptor(ByteArrayInputStream(this.encode()), this.sequence) PropertyDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
} }
1L -> { 1U -> {
HashTreeDescriptor(ByteArrayInputStream(this.encode()), this.sequence) HashTreeDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
} }
2L -> { 2U -> {
HashDescriptor(ByteArrayInputStream(this.encode()), this.sequence) HashDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
} }
3L -> { 3U -> {
KernelCmdlineDescriptor(ByteArrayInputStream(this.encode()), this.sequence) KernelCmdlineDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
} }
4L -> { 4U -> {
ChainPartitionDescriptor(ByteArrayInputStream(this.encode()), this.sequence) ChainPartitionDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
} }
else -> { else -> {
@ -107,7 +107,7 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0,
} }
init { 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.Helper
import cfig.ParamConfig import cfig.ParamConfig
import cfig.io.Struct import cfig.io.Struct3
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.Assert import org.junit.Assert
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File import java.io.File
@ -15,27 +14,27 @@ import java.security.MessageDigest
import java.util.regex.Pattern import java.util.regex.Pattern
open class BootImgHeader( open class BootImgHeader(
var kernelLength: Long = 0, var kernelLength: UInt = 0U,
var kernelOffset: Long = 0, var kernelOffset: UInt = 0U,
var ramdiskLength: Long = 0, var ramdiskLength: UInt = 0U,
var ramdiskOffset: Long = 0, var ramdiskOffset: UInt = 0U,
var secondBootloaderLength: Long = 0, var secondBootloaderLength: UInt = 0U,
var secondBootloaderOffset: Long = 0, var secondBootloaderOffset: UInt = 0U,
var recoveryDtboLength: Long = 0, var recoveryDtboLength: UInt = 0U,
var recoveryDtboOffset: Long = 0, var recoveryDtboOffset: ULong = 0UL,//Q
var dtbLength: Long = 0, var dtbLength: UInt = 0U,
var dtbOffset: Long = 0, 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 headerSize: UInt = 0U,
var headerVersion: Int = 0, var headerVersion: UInt = 0U,
var board: String = "", var board: String = "",
@ -51,40 +50,40 @@ open class BootImgHeader(
return return
} }
log.warn("BootImgHeader constructor") log.warn("BootImgHeader constructor")
val info = Struct(FORMAT_STRING).unpack(iS) val info = Struct3(FORMAT_STRING).unpack(iS)
Assert.assertEquals(20, info.size) 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") throw IllegalArgumentException("stream doesn't look like Android Boot Image Header")
} }
this.kernelLength = info[1] as Long this.kernelLength = info[1] as UInt
this.kernelOffset = info[2] as Long this.kernelOffset = info[2] as UInt
this.ramdiskLength = info[3] as Long this.ramdiskLength = info[3] as UInt
this.ramdiskOffset = info[4] as Long this.ramdiskOffset = info[4] as UInt
this.secondBootloaderLength = info[5] as Long this.secondBootloaderLength = info[5] as UInt
this.secondBootloaderOffset = info[6] as Long this.secondBootloaderOffset = info[6] as UInt
this.tagsOffset = info[7] as Long this.tagsOffset = info[7] as UInt
this.pageSize = (info[8] as Long).toInt() this.pageSize = info[8] as UInt
this.headerVersion = (info[9] as Long).toInt() this.headerVersion = info[9] as UInt
val osNPatch = (info[10] as Long).toInt() val osNPatch = info[10] as UInt
if (0 != osNPatch) { //treated as 'reserved' in this boot image if (0U != osNPatch) { //treated as 'reserved' in this boot image
this.osVersion = parseOsVersion(osNPatch shr 11) this.osVersion = parseOsVersion(osNPatch.toInt() shr 11)
this.osPatchLevel = parseOsPatchLevel(osNPatch and 0x7ff) this.osPatchLevel = parseOsPatchLevel((osNPatch and 0x7ff.toUInt()).toInt())
} }
this.board = Helper.toCString(info[11] as ByteArray).trim() this.board = info[11] as String
this.cmdline = Helper.toCString(info[12] as ByteArray) + Helper.toCString(info[14] as ByteArray) this.cmdline = (info[12] as String) + (info[14] as String)
this.hash = info[13] as ByteArray this.hash = info[13] as ByteArray
if (this.headerVersion > 0) { if (this.headerVersion > 0U) {
this.recoveryDtboLength = info[15] as Long this.recoveryDtboLength = info[15] as UInt
this.recoveryDtboOffset = info[16] as Long 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)) assert(this.headerSize.toInt() in intArrayOf(BOOT_IMAGE_HEADER_V2_SIZE, BOOT_IMAGE_HEADER_V1_SIZE))
if (this.headerVersion > 1) { if (this.headerVersion > 1U) {
this.dtbLength = info[18] as Long this.dtbLength = info[18] as UInt
this.dtbOffset = info[19] as Long this.dtbOffset = info[19] as ULong
} }
} }
@ -181,45 +180,45 @@ open class BootImgHeader(
private fun refresh() { private fun refresh() {
val param = ParamConfig() val param = ParamConfig()
//refresh kernel size //refresh kernel size
if (0L == this.kernelLength) { if (0U == this.kernelLength) {
throw java.lang.IllegalArgumentException("kernel size can not be 0") throw java.lang.IllegalArgumentException("kernel size can not be 0")
} else { } else {
this.kernelLength = File(param.kernel).length() this.kernelLength = File(param.kernel).length().toUInt()
} }
//refresh ramdisk size //refresh ramdisk size
if (0L == this.ramdiskLength) { if (0U == this.ramdiskLength) {
param.ramdisk = null param.ramdisk = null
} else { } else {
this.ramdiskLength = File(param.ramdisk).length() this.ramdiskLength = File(param.ramdisk).length().toUInt()
} }
//refresh second bootloader size //refresh second bootloader size
if (0L == this.secondBootloaderLength) { if (0U == this.secondBootloaderLength) {
param.second = null param.second = null
} else { } else {
this.secondBootloaderLength = File(param.second).length() this.secondBootloaderLength = File(param.second).length().toUInt()
} }
//refresh recovery dtbo size //refresh recovery dtbo size
if (0L == this.recoveryDtboLength) { if (0U == this.recoveryDtboLength) {
param.dtbo = null param.dtbo = null
} else { } else {
this.recoveryDtboLength = File(param.dtbo).length() this.recoveryDtboLength = File(param.dtbo).length().toUInt()
} }
//refresh recovery dtbo size //refresh recovery dtbo size
if (0L == this.dtbLength) { if (0U == this.dtbLength) {
param.dtb = null param.dtb = null
} else { } else {
this.dtbLength = File(param.dtb).length() this.dtbLength = File(param.dtb).length().toUInt()
} }
//refresh image hash //refresh image hash
val imageId = when (this.headerVersion) { val imageId = when (this.headerVersion) {
0 -> { 0U -> {
hashFileAndSize(param.kernel, param.ramdisk, param.second) hashFileAndSize(param.kernel, param.ramdisk, param.second)
} }
1 -> { 1U -> {
hashFileAndSize(param.kernel, param.ramdisk, param.second, param.dtbo) hashFileAndSize(param.kernel, param.ramdisk, param.second, param.dtbo)
} }
2 -> { 2U -> {
hashFileAndSize(param.kernel, param.ramdisk, param.second, param.dtbo, param.dtb) hashFileAndSize(param.kernel, param.ramdisk, param.second, param.dtbo, param.dtb)
} }
else -> { else -> {
@ -231,8 +230,8 @@ open class BootImgHeader(
fun encode(): ByteArray { fun encode(): ByteArray {
this.refresh() this.refresh()
val ret = Struct(FORMAT_STRING).pack( val ret = Struct3(FORMAT_STRING).pack(
"ANDROID!".toByteArray(), "ANDROID!",
//10I //10I
this.kernelLength, this.kernelLength,
this.kernelOffset, this.kernelOffset,
@ -245,28 +244,28 @@ open class BootImgHeader(
this.headerVersion, this.headerVersion,
(packOsVersion(this.osVersion) shl 11) or packOsPatchLevel(this.osPatchLevel), (packOsVersion(this.osVersion) shl 11) or packOsPatchLevel(this.osPatchLevel),
//16s //16s
this.board.toByteArray(), this.board,
//512s //512s
this.cmdline.substring(0, minOf(512, this.cmdline.length)).toByteArray(), this.cmdline.substring(0, minOf(512, this.cmdline.length)),
//32s //32b
this.hash!!, this.hash!!,
//1024s //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 //I
this.recoveryDtboLength, this.recoveryDtboLength,
//Q //Q
if (this.headerVersion > 0) this.recoveryDtboOffset else 0, if (this.headerVersion > 0U) this.recoveryDtboOffset else 0,
//I //I
when (this.headerVersion) { when (this.headerVersion) {
0 -> 0 0U -> 0
1 -> BOOT_IMAGE_HEADER_V1_SIZE 1U -> BOOT_IMAGE_HEADER_V1_SIZE
2 -> BOOT_IMAGE_HEADER_V2_SIZE 2U -> BOOT_IMAGE_HEADER_V2_SIZE
else -> java.lang.IllegalArgumentException("headerVersion ${this.headerVersion} illegal") else -> java.lang.IllegalArgumentException("headerVersion ${this.headerVersion} illegal")
}, },
//I //I
this.dtbLength, this.dtbLength,
//Q //Q
if (this.headerVersion > 1) this.dtbOffset else 0 if (this.headerVersion > 1U) this.dtbOffset else 0
) )
return ret return ret
} }
@ -278,7 +277,7 @@ open class BootImgHeader(
"10I" + "10I" +
"16s" + //board name "16s" + //board name
"512s" + //cmdline part 1 "512s" + //cmdline part 1
"32s" + //hash digest "32b" + //hash digest
"1024s" + //cmdline part 2 "1024s" + //cmdline part 2
"I" + //dtbo length [v1] "I" + //dtbo length [v1]
"Q" + //dtbo offset [v1] "Q" + //dtbo offset [v1]
@ -289,7 +288,7 @@ open class BootImgHeader(
const val BOOT_IMAGE_HEADER_V1_SIZE = 1648 const val BOOT_IMAGE_HEADER_V1_SIZE = 1648
init { 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) { class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) {
constructor() : this(null) constructor() : this(null)
val kernelPosition: Int val kernelPosition: UInt
get() { get() {
return getHeaderSize(this.pageSize) return getHeaderSize(this.pageSize)
} }
val ramdiskPosition: Int val ramdiskPosition: UInt
get() { get() {
return (kernelPosition + this.kernelLength + return (kernelPosition + this.kernelLength +
getPaddingSize(this.kernelLength.toInt(), this.pageSize)).toInt() getPaddingSize(this.kernelLength, this.pageSize))
} }
val secondBootloaderPosition: Int val secondBootloaderPosition: UInt
get() { get() {
return (ramdiskPosition + ramdiskLength + return ramdiskPosition + ramdiskLength +
getPaddingSize(ramdiskLength.toInt(), pageSize)).toInt() getPaddingSize(ramdiskLength, pageSize)
} }
val recoveryDtboPosition: Int val recoveryDtboPosition: ULong
get() { get() {
return (secondBootloaderPosition + secondBootloaderLength + return secondBootloaderPosition.toULong() + secondBootloaderLength +
getPaddingSize(secondBootloaderLength.toInt(), pageSize)).toInt() getPaddingSize(secondBootloaderLength, pageSize)
} }
val dtbPosition: Int val dtbPosition: ULong
get() { get() {
return (recoveryDtboPosition + dtbLength + return recoveryDtboPosition + recoveryDtboLength +
getPaddingSize(dtbLength.toInt(), pageSize)).toInt() getPaddingSize(recoveryDtboLength, pageSize)
} }
var signatureType: BootImgInfo.VerifyType? = null var signatureType: BootImgInfo.VerifyType? = null
@ -45,6 +45,15 @@ class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) {
return pad + 1648 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 { private fun getPaddingSize(position: Int, pageSize: Int): Int {
return (pageSize - (position and pageSize - 1)) and (pageSize - 1) return (pageSize - (position and pageSize - 1)) and (pageSize - 1)
} }
@ -59,39 +68,39 @@ class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) {
ret.addArgument(" --kernel ") ret.addArgument(" --kernel ")
ret.addArgument(param.kernel) ret.addArgument(param.kernel)
ret.addArgument(" --kernel_offset ") ret.addArgument(" --kernel_offset ")
ret.addArgument("0x" + java.lang.Long.toHexString(kernelOffset)) ret.addArgument("0x" + Integer.toHexString(kernelOffset.toInt()))
if (this.ramdiskLength > 0) { if (this.ramdiskLength > 0U) {
ret.addArgument(" --ramdisk ") ret.addArgument(" --ramdisk ")
ret.addArgument(param.ramdisk) ret.addArgument(param.ramdisk)
} }
ret.addArgument(" --ramdisk_offset ") ret.addArgument(" --ramdisk_offset ")
ret.addArgument("0x" + java.lang.Long.toHexString(ramdiskOffset)) ret.addArgument("0x" + Integer.toHexString(ramdiskOffset.toInt()))
if (this.secondBootloaderLength > 0) { if (this.secondBootloaderLength > 0U) {
ret.addArgument(" --second ") ret.addArgument(" --second ")
ret.addArgument(param.second) ret.addArgument(param.second)
} }
ret.addArgument(" --second_offset ") ret.addArgument(" --second_offset ")
ret.addArgument("0x" + java.lang.Long.toHexString(this.secondBootloaderOffset)) ret.addArgument("0x" + Integer.toHexString(this.secondBootloaderOffset.toInt()))
if (!board.isBlank()) { if (!board.isBlank()) {
ret.addArgument(" --board ") ret.addArgument(" --board ")
ret.addArgument(board) ret.addArgument(board)
} }
if (headerVersion > 0) { if (headerVersion > 0U) {
if (this.recoveryDtboLength > 0) { if (this.recoveryDtboLength > 0U) {
ret.addArgument(" --recovery_dtbo ") ret.addArgument(" --recovery_dtbo ")
ret.addArgument(param.dtbo) ret.addArgument(param.dtbo)
} }
} }
if (headerVersion > 1) { if (headerVersion > 1U) {
if (this.dtbLength > 0) { if (this.dtbLength > 0U) {
ret.addArgument("--dtb ") ret.addArgument("--dtb ")
ret.addArgument(param.dtb) ret.addArgument(param.dtb)
} }
ret.addArgument("--dtb_offset ") 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(" --pagesize ")
ret.addArgument(Integer.toString(pageSize)) ret.addArgument(Integer.toString(pageSize.toInt()))
ret.addArgument(" --cmdline ") ret.addArgument(" --cmdline ")
ret.addArgument(cmdline, false) ret.addArgument(cmdline, false)
if (!osVersion.isNullOrBlank()) { if (!osVersion.isNullOrBlank()) {
@ -103,7 +112,7 @@ class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) {
ret.addArgument(osPatchLevel) ret.addArgument(osPatchLevel)
} }
ret.addArgument(" --tags_offset ") ret.addArgument(" --tags_offset ")
ret.addArgument("0x" + java.lang.Long.toHexString(tagsOffset)) ret.addArgument("0x" + Integer.toHexString(tagsOffset.toInt()))
ret.addArgument(" --id ") ret.addArgument(" --id ")
ret.addArgument(" --output ") ret.addArgument(" --output ")
//ret.addArgument("boot.img" + ".google") //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) it.skip(footerBytes.size - 64L)
val footer = Footer(it) val footer = Footer(it)
println(footer.toString()) println(footer.toString())
assertEquals(1, footer.versionMajor) assertEquals(1U, footer.versionMajor)
assertEquals(0, footer.versionMinor) assertEquals(0U, footer.versionMinor)
assertEquals(512, footer.vbMetaSize) assertEquals(512U.toULong(), footer.vbMetaSize)
assertEquals(28983296, footer.vbMetaOffset) assertEquals(28983296U.toULong(), footer.vbMetaOffset)
assertEquals(28983296, footer.originalImageSize) assertEquals(28983296U.toULong(), footer.originalImageSize)
} }
} }

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

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

@ -7,7 +7,7 @@ import org.junit.Test
class AlgorithmsTest { class AlgorithmsTest {
@Test @Test
fun test() { fun test1() {
val alg = Algorithms.get("NONE")!! val alg = Algorithms.get("NONE")!!
Assert.assertEquals(Helper.toHexString(Algorithms.get("SHA256_RSA4096")!!.padding), 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.Helper
import cfig.io.Struct import cfig.io.Struct
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.Test import org.junit.Test
import org.junit.Assert.* import org.junit.Assert.*
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import kotlin.reflect.jvm.jvmName
class StructTest { 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 @Test
fun constructTest() { fun calcSizeTest() {
assertEquals(16, Struct("<2i4b4b").calcSize()) assertEquals(16, Struct("<2i4b4b").calcSize())
assertEquals(16, Struct("<Q8b").calcSize()) assertEquals(16, Struct("<Q8b").calcSize())
assertEquals(2, Struct(">h").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 shutil, os.path, json, subprocess, hashlib, glob
import unittest import unittest
successLogo = """
+----------------------------------+
| All test cases have PASSED |
+----------------------------------+
"""
def hashFile(fileName): def hashFile(fileName):
hasher = hashlib.md5() hasher = hashlib.md5()
with open(fileName, 'rb') as afile: 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 # from volunteers
verifySingleDir(resDir, "recovery_image_from_s-trace") verifySingleDir(resDir, "recovery_image_from_s-trace")
print(successLogo)

Loading…
Cancel
Save