Skip to content

Instantly share code, notes, and snippets.

@obfusk
Last active September 19, 2025 08:22
Show Gist options
  • Select an option

  • Save obfusk/31c332b884464cd8aa06ce1ba1583c05 to your computer and use it in GitHub Desktop.

Select an option

Save obfusk/31c332b884464cd8aa06ce1ba1583c05 to your computer and use it in GitHub Desktop.
APK Signing Block considerations

APK Signing Block considerations

Some considerations regarding the APK Signing Block and how F-Droid handles Reproducible Builds.

Block types

APK Signature Scheme Block

The signature part of the APK Signing Block can contain more than one signature.
AFAIK android and apksigner (unlike apksigtool) only check the one with the strongest supported signature algorithm ID, not all of them.
So it might be possible to hide some data in a signature that won't be checked normally.

Either way it's possible for the app to behave differently depending on which key it was signed with, though we can't do much about that.

Related: Android App Links.

Frosting and SourceStamp Blocks

These opaque Google-specific extensions have their own block types.
There is some documentation, code, and reverse engineering, but in the end we don't really know what's in there.

Dependency Info Block

Another opaque Google-specific extension.

"[...] data is compressed, encrypted by a Google Play signing key [...]"

Pretty much no useful search results

dependencyinfoblock-search

Any other block types

AFAIK these are simply ignored by apksigner etc.
So a developer could just create their own block type (or abuse an existing one) and put anything they want in there, including code.

Security

Android apps have access to their own APK file (e.g. I can run apksigcopier extract in Termux on Termux' own APK in /data/app/), so AFAIK an app could easily load some kind of code/instructions from the Signing Block, especially from something other than the APK Signature Scheme Block.

I haven't made a PoC for this yet, but it's on my TODO list somewhere.

What to do?

  • Apps signed by F-Droid should be "safe".
  • Apps using signatures in metadata could already have put something additional in the APKSigningBlock, but there is some accountability via git history.
  • Apps using Binaries: -- or the server we download the APK from -- could put anything additional in there.

I'd recommend at least "cleaning" and/or "linting" the APK Signing Block as part of our build process / CI, allowing only APK Signature Scheme Blocks (and verity zero padding), and ideally making sure all signatures in there are valid.

The downside to "cleaning" (when it actually removes something) is that it results in an APK file that verifies correctly, but will not have the same checksum as the original since the APK Signing Block differs.

Current apps using signatures in metadata seem to be "clean":

Output
$ for file in $( find -name APKSigningBlock ); do apksigtool clean --block "$file"; done | uniq -c
     37 nothing to clean

$ for file in $( find -name APKSigningBlock ); do apksigtool parse --block --json "$file" | jq -r '.pairs[].value._type'; done | sort | uniq -c
     72 APKSignatureSchemeBlock
     35 VerityPaddingBlock

However, two apps using Binaries: have a DependencyInfoBlock (encrypted by a Google Play signing key).

Output
$ for file in *.apk; do echo "==> $file"; apksigtool parse --json "$file" | jq -r '.pairs[].value._type'; echo; done
==> androdns.android.leetdreams.ch.androdns_15.apk
Error: No APK Signing Block.

==> androdns.android.leetdreams.ch.androdns_16.apk
Error: No APK Signing Block.

==> ch.admin.bag.covidcertificate.verifier_4040000.apk
APKSignatureSchemeBlock
DependencyInfoBlock
VerityPaddingBlock

==> ch.admin.bag.covidcertificate.verifier_4060000.apk
APKSignatureSchemeBlock
DependencyInfoBlock
VerityPaddingBlock

==> ch.admin.bag.covidcertificate.verifier_4070000.apk
APKSignatureSchemeBlock
DependencyInfoBlock
VerityPaddingBlock

==> ch.admin.bag.covidcertificate.wallet_4040000.apk
APKSignatureSchemeBlock
DependencyInfoBlock
VerityPaddingBlock

==> ch.admin.bag.covidcertificate.wallet_4060000.apk
APKSignatureSchemeBlock
DependencyInfoBlock
VerityPaddingBlock

==> ch.admin.bag.covidcertificate.wallet_4070000.apk
APKSignatureSchemeBlock
DependencyInfoBlock
VerityPaddingBlock

==> com.mishiranu.dashchan_1041.apk
APKSignatureSchemeBlock
VerityPaddingBlock

==> com.mishiranu.dashchan_1042.apk
APKSignatureSchemeBlock
VerityPaddingBlock

==> com.mishiranu.dashchan_1043.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> de.corona.tracing_2240202.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> de.corona.tracing_2250002.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> de.corona.tracing_2260004.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> de.schildbach.oeffi_120025.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> de.schildbach.oeffi_120100.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> de.schildbach.oeffi_120103.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> eu.bubu1.fdroidclassic_1014.apk
Error: No APK Signing Block.

==> eu.bubu1.fdroidclassic_1106.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> eu.bubu1.fdroidclassic_1110.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> info.guardianproject.checkey_101.apk
Error: No APK Signing Block.

==> nya.kitsunyan.foxydroid_2.apk
Error: No APK Signing Block.

==> nya.kitsunyan.foxydroid_3.apk
Error: No APK Signing Block.

==> nya.kitsunyan.foxydroid_4.apk
Error: No APK Signing Block.

==> org.briarproject.briar.android_10410.apk
APKSignatureSchemeBlock

==> org.briarproject.briar.android_10411.apk
APKSignatureSchemeBlock

==> org.briarproject.briar.android_10412.apk
APKSignatureSchemeBlock

==> org.jellyfin.mobile_2040199.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> org.jellyfin.mobile_2040299.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> org.jellyfin.mobile_2040499.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> rs.ltt.android_12.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> rs.ltt.android_13.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> rs.ltt.android_14.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> top.fumiama.copymanga_23.apk
APKSignatureSchemeBlock
APKSignatureSchemeBlock
VerityPaddingBlock

==> uk.co.keepawayfromfire.screens_6.apk
APKSignatureSchemeBlock

==> uk.co.keepawayfromfire.screens_7.apk
APKSignatureSchemeBlock

==> uk.co.keepawayfromfire.screens_8.apk
APKSignatureSchemeBlock

Related: #1055, #974, #1013, #935.

Edit 1: added apksigtool clean & apksigtool parse output for apps using signatures in metadata.
Edit 2: added apksigtool parse output for apps using Binaries: and Dependency Info Block section.


Dependency Info Block

When building your app using AGP 4.0.0 and higher, the plugin includes metadata that describes the library dependencies that are compiled into your app. [...] The data is compressed, encrypted by a Google Play signing key, and stored in the signing block of your release app. [...]

Removed Info

This bit was present in the old link that no longer has the info but isn't in the new one:

However, you can inspect the metadata yourself in the local intermediate build files in the following directory: <project>/<module>/build/outputs/sdk-dependencies/release/sdkDependency.txt.

Opt-out Instructions

android {
    dependenciesInfo {
        // Disables dependency metadata when building APKs.
        includeInApk = false
        // Disables dependency metadata when building Android App Bundles.
        includeInBundle = false
    }
}

Dependency Info Block isn't deterministic

If I make 2 copies of an unsigned APK and sign each separately with apksigner (using the same key) I get 2 bit-by-bit identical signed APKs.

$ cp -i unsigned.apk signed-1.apk
$ cp -i unsigned.apk signed-2.apk
$ apksigner sign ... signed-1.apk
Signed
$ apksigner sign ... signed-2.apk
Signed
$ shasum signed-*.apk
6e40428963a3e8c56f753848316fd620437126ef  signed-1.apk
6e40428963a3e8c56f753848316fd620437126ef  signed-2.apk

But if I use Android Studio (or a Gradle signing config) to build a signed APK (from the same commit and using the same signing key), it will add the Dependency Info Block (unless disabled), which is not deterministic, and thus I never get bit-by-bit identical signed APKs.

The only difference is the Dependency Info Block; cleaning the APK with apksigtool clean makes the APKs bit-by-bit identical as expected (and the signature stays valid).

IMO that does count as breaking RB. The signed APKs should be identical and they are not.

Which is at the very least causing confusion for devs trying to make their apps RB, as their builds are not identical and it is not at all obvious to them that it's "only" the Dependency Info Block that's different.

I'll report this as a bug to Google, but I don't really expect them to fix it any time soon.

And even if they do I still want the block gone since it contains encrypted data only Google can read.


Comments

v2-signed APKs should be bitwise identical after signature copying (without cleaning), so maybe we should not just verify the signature but also the shasum?


Links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment