diff --git a/README.md b/README.md index cb6e808..7161f7d 100644 --- a/README.md +++ b/README.md @@ -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 +
+ more ... + +Android version list https://source.android.com/source/build-numbers.html
+Android build-numbers https://source.android.com/setup/start/build-numbers + +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/
+boot\_signer
+https://android.googlesource.com/platform/system/extras
+mkbootimg
+https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/
+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
+ +
diff --git a/aosp/avb/avbtool.v1.2.py b/aosp/avb/avbtool.v1.2.py index 13f5594..1211df3 100755 --- a/aosp/avb/avbtool.v1.2.py +++ b/aosp/avb/avbtool.v1.2.py @@ -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. diff --git a/aosp/boot_signer/build.gradle.kts b/aosp/boot_signer/build.gradle.kts index 278c48a..53652be 100644 --- a/aosp/boot_signer/build.gradle.kts +++ b/aosp/boot_signer/build.gradle.kts @@ -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) { diff --git a/bbootimg/src/main/kotlin/avb/Avb.kt b/bbootimg/src/main/kotlin/avb/Avb.kt index 5ec49ec..890fa8d 100644 --- a/bbootimg/src/main/kotlin/avb/Avb.kt +++ b/bbootimg/src/main/kotlin/avb/Avb.kt @@ -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) diff --git a/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt b/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt index 0d77817..4c8decf 100644 --- a/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt +++ b/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt @@ -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)) } } diff --git a/bbootimg/src/test/kotlin/KeyUtilTest.kt b/bbootimg/src/test/kotlin/KeyUtilTest.kt index 8db5d18..d32c548 100644 --- a/bbootimg/src/test/kotlin/KeyUtilTest.kt +++ b/bbootimg/src/test/kotlin/KeyUtilTest.kt @@ -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)) } diff --git a/helper/build.gradle.kts b/helper/build.gradle.kts index fd87796..12a2696 100644 --- a/helper/build.gradle.kts +++ b/helper/build.gradle.kts @@ -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") diff --git a/helper/src/main/kotlin/cfig/helper/KeyHelper2.kt b/helper/src/main/kotlin/cfig/helper/KeyHelper2.kt index 4d64f4e..1330c8e 100644 --- a/helper/src/main/kotlin/cfig/helper/KeyHelper2.kt +++ b/helper/src/main/kotlin/cfig/helper/KeyHelper2.kt @@ -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 - */ + openssl dgst -sha256 + */ 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) - } } }