vbmeta.img: verify during unpacking

pull/53/head
cfig 4 years ago
parent bdd8b3972c
commit c46398dbb7
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -109,31 +109,28 @@ Your boot.img.signed and vbmeta.img.signd will be updated together.
Read [layout](doc/layout.md) of Android boot.img and vendor\_boot.img.
## References
boot\_signer
https://android.googlesource.com/platform/system/extras
cpio / fs\_config
https://android.googlesource.com/platform/system/core
https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt
AVB
https://android.googlesource.com/platform/external/avb/
mkbootimg
https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/
Android version list
https://source.android.com/source/build-numbers.html
kernel info extractor
https://android.googlesource.com/platform/build/+/refs/heads/master/tools/extract_kernel.py
mkdtboimg
https://android.googlesource.com/platform/system/libufdt/
libsparse
https://android.googlesource.com/platform/system/core/+/refs/heads/master/libsparse/
Android Nexus/Pixle factory images
https://developers.google.cn/android/images
<details>
<summary>more ...</summary>
Android version list https://source.android.com/source/build-numbers.html<br/>
Android build-numbers https://source.android.com/setup/start/build-numbers
cpio & fs\_config<br>
https://android.googlesource.com/platform/system/core<br/>
https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt<br/>
AVB<br/>
https://android.googlesource.com/platform/external/avb/<br/>
boot\_signer<br/>
https://android.googlesource.com/platform/system/extras<br/>
mkbootimg<br/>
https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/<br/>
kernel info extractor<br/>
https://android.googlesource.com/platform/build/+/refs/heads/master/tools/extract_kernel.py<br/>
mkdtboimg<br/>
https://android.googlesource.com/platform/system/libufdt/<br/>
libsparse<br/>
https://android.googlesource.com/platform/system/core/+/refs/heads/master/libsparse/<br/>
Android Nexus/Pixle factory images<br/>
https://developers.google.cn/android/images<br/>
</details>

@ -27,7 +27,6 @@
import argparse
import binascii
import bisect
import binascii
import hashlib
import json
import math
@ -578,7 +577,6 @@ def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
ha.update(header_blob)
ha.update(aux_blob)
computed_digest = ha.digest()
print("computed %s hash : %s" % (alg.hash_name, binascii.hexlify(computed_digest)))
if computed_digest != digest_blob:
return False
@ -588,7 +586,6 @@ def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
(num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
modulus_blob = pubkey_blob[8:8 + num_bits//8]
modulus = decode_long(modulus_blob)
print("modulus = %s" % modulus)
exponent = 65537
# We used to have this:
@ -2531,8 +2528,6 @@ class Avb(object):
if not verify_vbmeta_signature(header, vbmeta_blob):
raise AvbError('Signature check failed for {} vbmeta struct {}'
.format(alg_name, image_filename))
else:
print("Sig check done")
if key_blob:
# The embedded public key is in the auxiliary block at an offset.

@ -9,8 +9,8 @@ repositories {
}
dependencies {
implementation("org.bouncycastle:bcprov-jdk15on:1.57")
implementation("org.bouncycastle:bcpkix-jdk15on:1.57")
implementation("org.bouncycastle:bcprov-jdk15on:1.68")
implementation("org.bouncycastle:bcpkix-jdk15on:1.68")
}
val fatJar = task("fatJar", type = Jar::class) {

@ -10,6 +10,7 @@ import avb.desc.*
import cfig.helper.Helper
import cfig.helper.Helper.Companion.paddingWith
import cfig.helper.KeyHelper
import cfig.helper.KeyHelper2
import cfig.io.Struct3
import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.codec.binary.Hex
@ -22,6 +23,7 @@ import java.io.FileOutputStream
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardOpenOption
import java.security.PrivateKey
@OptIn(ExperimentalUnsignedTypes::class)
class Avb {
@ -273,38 +275,45 @@ class Avb {
}
}
//FIXME
//integrity check
val declaredAlg = Algorithms.get(ai.header!!.algorithm_type)
if (declaredAlg!!.public_key_num_bytes > 0) {
if (AuxBlob.encodePubKey(declaredAlg).contentEquals(ai.auxBlob!!.pubkey!!.pubkey)) {
log.warn("VERIFY: vbmeta is signed with the same key as us")
val calcHash =
AuthBlob.calcHash(ai.header!!.encode(), ai.auxBlob!!.encode(declaredAlg), declaredAlg.name)
val calcSig = AuthBlob.calcSignature(calcHash, declaredAlg.name)
if (Helper.toHexString(calcHash) != ai.authBlob!!.hash) {
log.error("calculated AuthBlob hash mismatch")
throw IllegalArgumentException("calculated AuthBlob hash mismatch")
} else {
log.info("VERIFY: AuthBlob hash matches")
log.info("VERIFY: signed with dev key: " + declaredAlg.defaultKey)
} else {
log.info("VERIFY: signed with release key")
}
val headerBlob = ByteArray(Header.SIZE).apply {
FileInputStream(image_file).use { fis ->
fis.skip(vbMetaOffset)
fis.read(this)
}
if (Helper.toHexString(calcSig) != ai.authBlob!!.signature) {
log.error("calculated AuthBlob signature mismatch")
throw IllegalArgumentException("calculated AuthBlob signature mismatch")
}
val auxBlob = ByteArray(vbMetaHeader.auxiliary_data_block_size.toInt()).apply {
FileInputStream(image_file).use { fis ->
fis.skip(auxBlockOffset)
fis.read(this)
}
}
val calcHash = Helper.join(declaredAlg.padding, AuthBlob.calcHash(headerBlob, auxBlob, declaredAlg.name))
val readHash = Helper.join(declaredAlg.padding, Helper.fromHexString(ai.authBlob!!.hash!!))
if (calcHash.contentEquals(readHash)) {
log.info("VERIFY: vbmeta hash... PASS")
val readPubKey = KeyHelper.decodeRSAkey(ai.auxBlob!!.pubkey!!.pubkey)
val hashFromSig = KeyHelper2.rawRsa(readPubKey, Helper.fromHexString(ai.authBlob!!.signature!!))
if (hashFromSig.contentEquals(readHash)) {
log.info("VERIFY: vbmeta signature... PASS")
} else {
log.info("VERIFY: AuthBlob signature matches")
log.warn("read=" + Helper.toHexString(readHash) + ", calc=" + Helper.toHexString(calcHash))
log.warn("VERIFY: vbmeta signature... FAIL")
}
} else {
val custPubKey = KeyHelper.decodeRSAkey(ai.auxBlob!!.pubkey!!.pubkey)
log.warn("VERIFY: vbmeta is signed with different key as us")
log.debug("modulus :" + custPubKey.modulus)
log.debug("exponent :" + custPubKey.publicExponent)
log.warn("read=" + ai.authBlob!!.hash!! + ", calc=" + Helper.toHexString(calcHash))
log.warn("VERIFY: vbmeta hash... FAIL")
}
//FIXME
ai.auxBlob!!.pubkey
} else {
log.debug("no key for current algorithm")
log.warn("no signing key for current algorithm")
}
//FIXME
if (dumpFile) {
ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(jsonFile), ai)

@ -45,7 +45,7 @@ data class AuthBlob(
byteArrayOf()
} else {
val k = KeyHelper.parse(Files.readAllBytes(Paths.get(alg.defaultKey.replace(".pem", ".pk8")))) as PrivateKey
KeyHelper2.rawSign(k, Helper.join(alg.padding, hash))
KeyHelper2.rawRsa(k, Helper.join(alg.padding, hash))
}
}

@ -76,7 +76,7 @@ class KeyUtilTest {
"28e17bc57406650ed78785fd558e7c1861cc4014c900d72b61c03cdbab1039e713b5bb19b556d04d276b46aae9b8a3999ccbac533a1cce00f83cfb83e2beb35ed7329f71ffec04fc2839a9b44e50abd66ea6c3d3bea6705e93e9139ecd0331170db18eba36a85a78bc49a5447260a30ed19d956cb2f8a71f6b19e57fdca43e052d1bb7840bf4c3efb47111f4d77764236d2e013fbf3b2577e4a3e01c9d166a5e890ef96210882e6e88ceca2fe3a2201f4961210d4ec6167f5dfd0e038e4a146f960caecab7d15ba65f6edcf5dbd25f5af543cfb8da4338bdbc872eec3f8e72aa8db679099e70952d3f7176c0b9111bf20ad1390eab1d09a859105816fdf92fbb"
val privkFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey.replace("pem", "pk8")
val k = KeyHelper.parse(Files.readAllBytes(Paths.get(privkFile))) as PrivateKey
val encData = KeyHelper2.rawSign(k, data)
val encData = KeyHelper2.rawRsa(k, data)
assertEquals(expectedSig, Helper.toHexString(encData))
}

@ -17,7 +17,7 @@ dependencies {
implementation("org.slf4j:slf4j-api:1.7.30")
implementation("org.slf4j:slf4j-simple:1.7.30")
implementation("org.apache.commons:commons-exec:1.3")
implementation("org.bouncycastle:bcprov-jdk15on:1.57")
implementation("org.bouncycastle:bcprov-jdk15on:1.68")
implementation("org.apache.commons:commons-compress:1.20")
implementation("org.tukaani:xz:1.8")

@ -8,23 +8,28 @@ import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.security.MessageDigest
import java.security.PrivateKey
import java.security.PublicKey
import java.security.Signature
import java.util.*
import javax.crypto.Cipher
class KeyHelper2 {
companion object {
private val log = LoggerFactory.getLogger(KeyHelper2::class.java)
//inspired by
// https://stackoverflow.com/questions/40242391/how-can-i-sign-a-raw-message-without-first-hashing-it-in-bouncy-castle
// "specifying Cipher.ENCRYPT mode or Cipher.DECRYPT mode doesn't make a difference;
// both simply perform modular exponentiation"
fun rawSign(privk: java.security.PrivateKey, data: ByteArray): ByteArray {
/* inspired by
https://stackoverflow.com/questions/40242391/how-can-i-sign-a-raw-message-without-first-hashing-it-in-bouncy-castle
"specifying Cipher.ENCRYPT mode or Cipher.DECRYPT mode doesn't make a difference;
both simply perform modular exponentiation"
python counterpart:
import Crypto.PublicKey.RSA
key = Crypto.PublicKey.RSA.construct((modulus, exponent))
vRet = key.verify(decode_long(padding_and_digest), (decode_long(sig_blob), None))
print("verify padded digest: %s" % binascii.hexlify(padding_and_digest))
print("verify sig: %s" % binascii.hexlify(sig_blob))
print("X: Verify: %s" % vRet)
*/
fun rawRsa(key: java.security.Key, data: ByteArray): ByteArray {
return Cipher.getInstance("RSA/ECB/NoPadding").let { cipher ->
cipher.init(Cipher.ENCRYPT_MODE, privk)
cipher.init(Cipher.ENCRYPT_MODE, key)
cipher.update(data)
cipher.doFinal()
}
@ -67,8 +72,8 @@ class KeyHelper2 {
}
/*
openssl dgst -sha256 <file>
*/
openssl dgst -sha256 <file>
*/
fun sha256(inData: ByteArray): ByteArray {
return MessageDigest.getInstance("SHA-256").digest(inData)
}
@ -83,41 +88,5 @@ class KeyHelper2 {
fun sha256rsa(inData: ByteArray, inKey: java.security.PrivateKey): ByteArray {
return rsa(sha256(inData), inKey)
}
fun sign(inData: ByteArray, privateKey: PrivateKey): String {
val signature = Signature.getInstance("SHA256withRSA").let {
it.initSign(privateKey)
it.update(inData)
it.sign()
}
return Base64.getEncoder().encodeToString(signature)
}
fun verify(inData: ByteArray, signature: ByteArray, pubKey: PublicKey): Boolean {
return Signature.getInstance("SHA256withRSA").let {
it.initVerify(pubKey)
it.update(inData)
it.verify(signature)
}
}
fun verify(inData: ByteArray, base64Signature: String, pubKey: PublicKey): Boolean {
val signatureBytes = Base64.getDecoder().decode(base64Signature)
return Signature.getInstance("SHA256withRSA").let {
it.initVerify(pubKey)
it.update(inData)
it.verify(signatureBytes)
}
}
fun verify2(inData: ByteArray, encrypedHash: ByteArray, inKey: java.security.PublicKey): Boolean {
val calcHash = sha256(inData)
val decrypedHash = Cipher.getInstance("RSA").let {
it.init(Cipher.DECRYPT_MODE, inKey)
it.doFinal(encrypedHash)
}
return calcHash.contentEquals(decrypedHash)
}
}
}

Loading…
Cancel
Save