enhancement: support PropertyDescriptor; support flashing vbmeta.img

pull/20/head
cfig 7 years ago
parent ec00936463
commit 1f7476d884
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -2,7 +2,7 @@
[![Build Status](https://travis-ci.org/cfig/Android_boot_image_editor.svg?branch=master)](https://travis-ci.org/cfig/Android_boot_image_editor)
[![License](http://img.shields.io/:license-apache-blue.svg?style=flat-square)](http://www.apache.org/licenses/LICENSE-2.0.html)
This tool focuses on editing Android boot.img(also recovery.img and recovery-two-step.img).
This tool focuses on editing Android boot.img(also recovery.img, recovery-two-step.img and vbmeta.img).
## Prerequisite
#### Host OS requirement:

@ -285,6 +285,9 @@ class Avb {
descriptors.forEach {
when (it) {
is PropertyDescriptor -> {
ai.auxBlob!!.propertyDescriptor.add(it)
}
is HashDescriptor -> {
ai.auxBlob!!.hashDescriptors.add(it)
}

@ -1,6 +1,5 @@
package cfig
import avb.alg.Algorithms
import cfig.io.Struct
import com.google.common.math.BigIntegerMath
import org.apache.commons.codec.binary.Hex
@ -24,6 +23,7 @@ import java.nio.file.Paths
import java.security.KeyFactory
import java.security.PrivateKey
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.RSAPrivateKeySpec
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import javax.crypto.Cipher
@ -183,9 +183,7 @@ class Helper {
https://android.googlesource.com/platform/external/avb/+/master/libavb/avb_crypto.h#158
*/
fun encodeRSAkey(key: ByteArray): ByteArray {
val p2 = PemReader(InputStreamReader(ByteArrayInputStream(key))).readPemObject()
Assert.assertEquals("RSA PRIVATE KEY", p2.type)
val rsa = RSAPrivateKey.getInstance(p2.content)
val rsa = KeyUtil.parsePemPrivateKey(ByteArrayInputStream(key))
Assert.assertEquals(65537.toBigInteger(), rsa.publicExponent)
val numBits: Int = BigIntegerMath.log2(rsa.modulus, RoundingMode.CEILING)
log.debug("modulus: " + rsa.modulus)
@ -213,7 +211,7 @@ class Helper {
// "specifying Cipher.ENCRYPT mode or Cipher.DECRYPT mode doesn't make a difference;
// both simply perform modular exponentiation"
fun rawSign(keyPath: String, data: ByteArray): ByteArray {
val privk = Helper.readPrivateKey(keyPath)
val privk = KeyUtil.parsePk8PrivateKey(Files.readAllBytes(Paths.get(keyPath)))
val cipher = Cipher.getInstance("RSA/ECB/NoPadding").apply {
this.init(Cipher.ENCRYPT_MODE, privk)
this.update(data)
@ -257,12 +255,6 @@ class Helper {
}
}
@Throws(Exception::class)
fun readPrivateKey(filename: String): PrivateKey {
val spec = PKCS8EncodedKeySpec(Files.readAllBytes(Paths.get(filename)))
return KeyFactory.getInstance("RSA").generatePrivate(spec)
}
private val log = LoggerFactory.getLogger("Helper")
}
}

@ -0,0 +1,47 @@
package cfig
import org.bouncycastle.asn1.pkcs.RSAPrivateKey
import org.bouncycastle.util.io.pem.PemReader
import java.io.InputStream
import java.io.InputStreamReader
import java.math.BigInteger
import java.security.KeyFactory
import java.security.PrivateKey
import java.security.PublicKey
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.RSAPrivateKeySpec
import java.security.spec.RSAPublicKeySpec
class KeyUtil {
companion object {
@Throws(IllegalArgumentException::class)
fun parsePemPrivateKey(inputStream: InputStream): RSAPrivateKey {
val p = PemReader(InputStreamReader(inputStream)).readPemObject()
if ("RSA PRIVATE KEY" != p.type) {
throw IllegalArgumentException("input is not valid 'RSA PRIVATE KEY'")
}
return RSAPrivateKey.getInstance(p.content)
}
fun parsePemPrivateKey2(inputStream: InputStream): PrivateKey {
val rsa = KeyUtil.parsePemPrivateKey(inputStream)
return generateRsaPrivateKey(rsa.modulus, rsa.privateExponent)
}
@Throws(Exception::class)
fun parsePk8PrivateKey(inputData: ByteArray): PrivateKey {
val spec = PKCS8EncodedKeySpec(inputData)
return KeyFactory.getInstance("RSA").generatePrivate(spec)
}
@Throws(Exception::class)
private fun generateRsaPublicKey(modulus: BigInteger, publicExponent: BigInteger): PublicKey {
return KeyFactory.getInstance("RSA").generatePublic(RSAPublicKeySpec(modulus, publicExponent))
}
@Throws(Exception::class)
private fun generateRsaPrivateKey(modulus: BigInteger, privateExponent: BigInteger): PrivateKey {
return KeyFactory.getInstance("RSA").generatePrivate(RSAPrivateKeySpec(modulus, privateExponent))
}
}
}

@ -19,6 +19,7 @@ class AVBInfo(var header: Header? = null,
data class AuxBlob(
var pubkey: PubKeyInfo? = null,
var pubkeyMeta: PubKeyMetadataInfo? = null,
var propertyDescriptor: MutableList<PropertyDescriptor> = mutableListOf(),
var hashTreeDescriptor: MutableList<HashTreeDescriptor> = mutableListOf(),
var hashDescriptors: MutableList<HashDescriptor> = mutableListOf(),
var kernelCmdlineDescriptor: MutableList<KernelCmdlineDescriptor> = mutableListOf(),

@ -0,0 +1,10 @@
package avb.desc
class ChainPartitionDescriptor {
companion object {
const val TAG = 4L
const val RESERVED = 64
const val SIZE = 28 + RESERVED
const val FORMAT_STRING = "!2Q3L"
}
}

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

@ -32,6 +32,9 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0,
fun analyze(): Any {
return when (this.tag) {
0L -> {
PropertyDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
}
1L -> {
HashTreeDescriptor(ByteArrayInputStream(this.encode()), this.sequence)
}

@ -1,9 +1,15 @@
import avb.alg.Algorithms
import cfig.Helper
import cfig.KeyUtil
import com.google.common.math.BigIntegerMath
import org.apache.commons.codec.binary.Hex
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Assert.*
import org.junit.Test
import java.math.BigInteger
import java.math.RoundingMode
import java.nio.file.Files
import java.nio.file.Paths
import java.security.KeyFactory
import java.security.KeyPairGenerator
import java.security.Security
@ -11,6 +17,11 @@ import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher
import java.security.spec.RSAPublicKeySpec
import java.security.PublicKey
import java.security.spec.RSAPrivateKeySpec
import java.security.PrivateKey
class HelperTest {
@Test
@ -35,7 +46,8 @@ class HelperTest {
fun test3() {
val data = Hex.decodeHex("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004206317a4c8d86accc8258c1ac23ef0ebd18bc3301033")
val signature = Signature.getInstance("NONEwithRSA")
val k = Helper.readPrivateKey("../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey.replace("pem", "pk8"))
val keyFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey.replace("pem", "pk8")
val k = KeyUtil.parsePk8PrivateKey(Files.readAllBytes(Paths.get(keyFile)))
signature.initSign(k)
signature.update(data)
println("data size " + data.size)
@ -75,4 +87,46 @@ class HelperTest {
val encryptedText = Hex.encodeHexString(cipher.doFinal())
println(encryptedText)
}
@Test
fun testRSA() {
// val r = BigIntegerMath.log2(BigInteger.valueOf(1024), RoundingMode.CEILING)
// println(r)
// println(BigInteger.valueOf(1024).mod(BigInteger.valueOf(2)))
val p = BigInteger.valueOf(3)
val q = BigInteger.valueOf(7)
val modulus = p.multiply(q)
val keyLength = BigIntegerMath.log2(modulus, RoundingMode.CEILING)
println("keyLength = $keyLength")
//r = phi(n) = phi(p) * phi(q) = (p - 1)*(q - 1)
val r = (p.subtract(BigInteger.ONE)).multiply(q - BigInteger.ONE)
//r ~ e
//e is released as the public key exponent
//most commonly e = 2^16 + 1 = 65,537
val e = BigInteger.valueOf(5)
//(d * e).mod(r) == 1
//d is kept as the private key exponent
val d = e.modInverse(r)
println("p = $p, q = $q, modulus = $modulus , r = $r, e = $e, d = $d")
assertEquals(1, d.multiply(e).mod(r).toInt())
//private key: (modulus, d), d is calculated
//pub key: (modulus, e) , e is chosen
val clearMsg = BigInteger.valueOf(10)
val encMsg = clearMsg.pow(e.toInt()).mod(modulus)
println("clear: $clearMsg, enc: $encMsg")
val decMsg = clearMsg
}
fun gcd(a: BigInteger, b: BigInteger): BigInteger {
return if (b == BigInteger.ZERO) {
a
} else gcd(b, a.mod(b))
}
}

@ -0,0 +1,21 @@
import avb.alg.Algorithms
import cfig.KeyUtil
import org.apache.commons.codec.binary.Hex
import org.junit.Assert.*
import org.junit.Test
import java.io.FileInputStream
import java.nio.file.Files
import java.nio.file.Paths
class KeyUtilTest {
@Test
fun parseKeys() {
val keyFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey
val k = KeyUtil.parsePk8PrivateKey(Files.readAllBytes(Paths.get(keyFile.replace("pem", "pk8"))))
val k2 = KeyUtil.parsePemPrivateKey2(FileInputStream(keyFile))
println(Hex.encodeHexString(k.encoded))
println(Hex.encodeHexString(k2.encoded))
}
}

@ -108,6 +108,12 @@ void updateBootImage(String activeImg) {
case "recovery.img":
flashTarget = "/dev/block/by-name/recovery";
break;
case "vbmeta.img":
flashTarget = "/dev/block/by-name/vbmeta";
if (!new File(activeImg + ".signed").exists()) {
return;
}
break;
}
Run("adb root")
Run("adb push " + activeImg + ".signed /cache/")
@ -120,6 +126,7 @@ void updateBootImage(String activeImg) {
task flash {
doLast {
updateBootImage(activeImg)
updateBootImage("vbmeta.img")
}
}

Loading…
Cancel
Save