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)
- }
}
}