tangem-app-android-audited/APKANALYSIS.md

13 KiB

Binary Artifact Analysis

Tangem Android Wallet — Official Release APK vs. Build from Audited Source


Subject Comparison of Tangem's official v6.1.2 release APK against a build produced from the audited source
Companion to AUDIT.md — static source review of commit 358144ee61a
Analysis date 17 August 2026
Analysis type Static binary inspection (manifest, signing, package structure). No decompilation, no execution
Objective Determine whether the officially distributed binary can be confirmed to originate from the audited source

1. Executive Summary

The official APK cannot be verified as originating from the audited source, and no available technique would allow it. This is a structural property of how Tangem builds and hardens its release artifact, not a deficiency of this analysis or an indication of wrongdoing.

What can be established is weaker but not worthless: every externally observable property of the official binary that could be compared against a build from the audited source matches. That rules out crude substitution or repackaging. It does not, and cannot, rule out a targeted modification concealed beneath the protection layer.

The comparison additionally surfaced two factual errors in AUDIT.md, both of which overstate the security posture of the shipped application. They are recorded in §5 and have been corrected in the audit report.

Verdict

UNVERIFIABLE — CONSISTENT. Provenance of the official binary cannot be established by any means available to a third party. All comparable attributes are consistent with the audited source. Absence of contradiction is not evidence of correspondence.


2. Artifacts Compared

Official release Build from audited source
Filename app-release-protected-1819-6.1.2.apk app-google-debug.apk
Origin github.com/tangem/tangem-app-android releases, tag v6.1.2 ./gradlew :app:assembleGoogleDebug at 358144ee61a
Size 134,544,995 bytes 176,908,283 bytes
SHA-256 6ed4ac317f2f5bdccd5bb1cec9d6733207261e6b4f823a4de883e646872166a1 5df4c81755bc423ed39e92499830d789b309561211c62091585fa9038adfe5cd
Package com.tangem.wallet com.tangem.wallet.debug
Version 6.1.2 (code 1819) 1.0.0-SNAPSHOT (code 1)
Build type release, minified, DexProtector-hardened debug, unminified, unprotected
Signer L=Zug, O=Tangem AG Committed dummy debug keystore
Archive entry dates 2026-08-13 2026-08-17

The size difference is expected and runs counter to intuition: the debug build is larger because it is not minified by R8, despite carrying no protection layer.


3. Why Provenance Cannot Be Established

Four independent obstacles apply. Any one alone would be sufficient to defeat verification.

3.1 The release is commercially hardened

The official APK ships Licel DexProtector:

lib/arm64-v8a/libdexprotector.so     446,117 bytes
lib/armeabi-v7a/libdexprotector.so   394,517 bytes
assets/assets.dat                    229,499 bytes
assets/ict.dat                        10,853 bytes

DexProtector encrypts and obfuscates DEX bytecode. The transformation is keyed per build and is explicitly designed to prevent the analysis that verification would require. Comparing bytecode between the two artifacts is not merely impractical — it is precluded by design.

3.2 The compared artifacts are different build types

The official build passes through R8 minification, obfuscation, and resource shrinking. The reference build does not. Class names, method signatures, and the DEX file layout diverge for reasons entirely unrelated to source content: 54 DEX files versus 68.

3.3 The source tree does not correspond to the released version

The repository carries no version of its own. AppConfig.versionCode is 1, and VersionNameProvider derives the name from the current git branch, falling back to 1.0.0-SNAPSHOT. Both values are injected by CI via -PversionCode / -PversionName.

Consequently nothing in the tree identifies which release a given commit produced. The evidence indicates the audited commit is newer than v6.1.2: archive entries in the official APK are dated 2026-08-13, whereas the audited commits are dated 2026-08-14. The two artifacts therefore do not represent the same source state, and could not be expected to match even in principle.

3.4 The build is not reproducible by a third party

Independent of the above, an outside party cannot reconstruct the official build:

  • core/ui/ds-tokens is a submodule gitlink with no .gitmodules mapping anywhere in the repository's 16,087-commit history, so it cannot be resolved (see AUDIT.md O-3)
  • All third-party API keys in config_prod.json are PLACEHOLDER; the official build embeds real values, compiled in at build time by GenerateEnvironmentConfigTask
  • Release signing requires a keystore that is deliberately not published

Tangem publishes no reproducible-build attestation, and the artifact's construction is incompatible with one.


4. Consistency Findings

The following were compared and match. Each is a property an attacker repackaging or substituting the application would find difficult to preserve.

4.1 Permission set — identical

Both APKs declare exactly 23 permissions. After normalising the debug package prefix, a sorted diff of the two sets is empty.

This is the most informative single result. The comparison was made against a binary built from the audited source, so any permission present in Tangem's distributed build but not produced by that source would be a direct indicator of divergence. There is none.

Both sets also exclude location and microphone permissions, confirming that the tools:node="remove" stripping described in AUDIT.md §3.6 is genuine and survives into the shipped artifact.

4.2 Native libraries — identical, plus protection runtime

Count Detail
Build from audited source 14 see below
Official release 16 the same 14, plus libdexprotector.so and libalice.so
libTrustWalletCore.so · libsecp256k1-jni.so · libTrezorCrypto.so · libargon2.so
libblst.so · libsqlcipher.so · libuniffi_yttrium.so · libtoolChecker.so
libtensorflowlite_jni.so · libbarhopper_v3.so · libjnidispatch.so
libimage_processing_util_jni.so · libandroidx.graphics.path.so · libsurface_util_jni.so

The audited source reproduces the official release's native library set exactly. The only additions are the protection layer. libalice.so was not independently identified; it appears only in the protected build alongside libdexprotector.so, which is consistent with it forming part of the DexProtector runtime, but this was not confirmed.

4.3 Build configuration and identity

  • targetSdkVersion 36 and compileSdkVersion 36 in both, matching the source configuration
  • Package com.tangem.wallet (reference build carries the expected .debug suffix)
  • Official APK signed by L=Zug, O=Tangem AG, certificate SHA-256 e0fe798927cf94709b9d439c9ce728a90a2891698cdff31a4c4d63435b946748, coherent with Tangem AG's registration in Zug, Switzerland

Note on the signing certificate: this establishes only that the holder of that key signed the artifact. It is a first-observation baseline, not an identity proof. Its value is comparative — future releases can be checked against this digest to detect a signing-key change.


5. Discrepancies Against AUDIT.md

Both findings below arise from the same root cause: the audit examined the source tree, and its conclusions were phrased as though they described the shipped application. Neither indicates malicious behaviour. Both have been corrected in AUDIT.md.

D-1 — Permission set understated (AUDIT.md §3.6)

The audit lists nine permissions as the "complete permission set across all manifests" and states that external storage and RECEIVE_BOOT_COMPLETED are absent.

The nine are accurate for the hand-written manifests (app/src/main/ and app/src/huawei/), which were verified to declare exactly that set. However the merged manifest — in Tangem's release and in the build from the audited source alike — contains 23. The fourteen not accounted for:

READ_EXTERNAL_STORAGE · WRITE_EXTERNAL_STORAGE · WRITE_SETTINGS
RECEIVE_BOOT_COMPLETED · FOREGROUND_SERVICE · USE_FINGERPRINT · POST_NOTIFICATIONS
BIND_GET_INSTALL_REFERRER_SERVICE · ACCESS_ADSERVICES_ATTRIBUTION
ACCESS_ADSERVICES_AD_ID · c2dm.RECEIVE · DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION
com.samsung.android.mapsagent.permission.READ_APP_INFO
com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA

These are introduced by manifest merging from bundled third-party SDKs — the same mechanism the audit correctly credits Tangem for defending against with tools:node="remove". The characterisation "permissions MINIMISED" and "the set contains nothing a wallet does not require" is not supported by the artifact as shipped. WRITE_SETTINGS and RECEIVE_BOOT_COMPLETED in particular warrant explanation rather than omission.

D-2 — "No native code" is true of the repository, false of the application (AUDIT.md §3.1)

The audit states "No .so libraries, no JNI, no NDK build inputs," and answers No to the methodology question "Does it ship native binaries or opaque blobs?"

This is correct for the source tree — no .so file is committed. It is incorrect for the application, which bundles fourteen native libraries even in the unprotected build (§4.2).

The distinction is material because several are security-critical: libTrustWalletCore.so, libsecp256k1-jni.so, and libTrezorCrypto.so perform key derivation and signing; libargon2.so provides key-stretching; libsqlcipher.so provides at-rest database encryption. These arrive through the pre-built Maven dependencies that AUDIT.md §6.1 places outside the review's scope, so the audit's scope statement does technically cover them. But a reader reaching "ships native binaries: No" in the summary table will draw the opposite conclusion about the application they are installing.


6. What This Analysis Does and Does Not Establish

Does establish:

  • The official v6.1.2 APK is signed by Tangem AG and structured as the audited source predicts
  • Its permission surface and native library set are exactly reproduced by building the audited source, with no unexplained additions
  • Gross tampering — repackaging, permission injection, substitution of a different application — is ruled out
  • The privacy hardening claimed in AUDIT.md §3.6 is present in the distributed artifact

Does not establish:

  • That the official APK was built from the audited source, or from any particular source
  • That the DEX bytecode is free of modification. DexProtector renders this unexaminable
  • Anything about the pre-built Tangem SDKs, which perform mnemonic encryption and at-rest key storage and remain unreviewed (AUDIT.md §6.1)
  • That any future release will share these properties

A user installing the official APK is extending trust to Tangem's build pipeline and signing key. Nothing available to a third party can substitute for that trust. This analysis narrows the range of undetected substitutions; it does not eliminate it.


7. Method

All inspection used Android SDK build-tools 36.0.0.

# Manifest, version, permissions
aapt2 dump badging <apk>

# Signing certificate
apksigner verify --print-certs <apk>

# Package structure, native libraries, DEX count
unzip -l <apk>

Permission sets were extracted, normalised for the debug package prefix, sorted, and compared with diff. Native library sets were compared with comm after stripping ABI directory components.

No decompilation, deobfuscation, or execution was performed. No attempt was made to defeat DexProtector.


8. Limitations

  1. Static inspection of archive metadata only. Application behaviour was not observed.
  2. Version mismatch. The reference build derives from a commit dated one day after the official artifact. Differences attributable to intervening source changes cannot be distinguished from differences attributable to anything else.
  3. Reference build was not fully faithful. It was produced with verifyDesignTokens skipped (the ds-tokens submodule is unresolvable) and with PLACEHOLDER API keys. Neither affects the manifest or native library comparisons on which the findings rest.
  4. Single point in time. Applies to v6.1.2 build 1819 only. Re-verification is required for every subsequent release, and the certificate digest in §4.3 is the recommended anchor.

Binary analysis of app-release-protected-1819-6.1.2.apk against a reference build of commit 358144ee61a. Companion to AUDIT.md; findings D-1 and D-2 have been applied to that report.