mSzyfr v26.03.3 vs Element X v26.03.2

Same-Platform Binary Comparison — armeabi-v7a / Play Store builds
Generated 2026-04-13 — All data measured from actual binaries
bartoszsroka.com

VERDICT: Same Codebase, Different Builds

mSzyfr and Element X share the same source code but are compiled as independent builds with different compiler toolchains, SDK versions, optimization levels, and configuration. No binary files are byte-identical. mSzyfr ships an unstripped, debug-symbol-inclusive native Rust library — a significant operational security concern for a government app.

6 / 13
Byte-identical native libraries
6,500
Shared DEX classes (of ~23.5K each)
413,457
Debug symbols in mSzyfr (vs 0)
2.26×
mSzyfr Matrix SDK size vs ElementX

1. Build Identity

mSzyfr v26.03.3

Packagepl.nask.mszyfr
Version26.03.3 (code 202603030)
DeveloperNASK (Polish Gov Research Institute)
Rust compilerrustc 1.91.1 (2025-11-07)
Clang19.0.1 (Android r530567e)
LinkerLLD 19.0.1
R8 obfuscationYES — 12,882 obfuscated classes
Native symbolsUNSTRIPPED — 413K symbols

Element X v26.03.2

Packageio.element.android.x
Version26.03.2 (code 202603021)
DeveloperElement (New Vector Ltd)
Rust compilerrustc 1.93.1 (2026-02-11)
Clang21.0.0 (Android r563880c)
LinkerLLD 21.0.0
R8 obfuscationNO — 0 obfuscated classes
Native symbolsSTRIPPED — 0 symbols
Key Finding: mSzyfr uses an older Rust toolchain (1.91.1 vs 1.93.1) and an older Clang (19.0.1 vs 21.0.0). The Rust SDK is compiled with debug symbols included (413,457 symbols, 25.1 MB of symbol tables), while Element X's is properly stripped. This is paradoxical — NASK applied R8 obfuscation to the Java/Kotlin layer but left the native Rust layer completely unstripped, exposing all internal function names.

2. Native Library Comparison (armeabi-v7a)

SHA-256 Hash Comparison — Same Architecture, Direct Binary Match

LibrarymSzyfr SizeElementX SizeVerdict
libandroidx.graphics.path.so7,252 B7,252 BIDENTICAL
libdatastore_shared_counter.so53,412 B53,412 BIDENTICAL
libimage_processing_util_jni.so24,448 B24,448 BIDENTICAL
libjnidispatch.so126,496 B126,496 BIDENTICAL
libopuscodec.so506,040 B506,040 BIDENTICAL
libsurface_util_jni.so3,460 B3,460 BIDENTICAL
libmatrix_sdk_ffi.so74.7 MB32.9 MBDIFFERENT (2.26×)
libmaplibre.so9.6 MB9.6 MBDIFFERENT
libsqlcipher.so4.1 MB4.1 MBDIFFERENT
libuniffi_wysiwyg_composer.so2.2 MB2.2 MBDIFFERENT
libzxingcpp_android.so1.2 MB1.2 MBDIFFERENT
libsentry.so483 KBMISSING from mSzyfr
libsentry-android.so12 KBMISSING from mSzyfr

Native Library Match Summary

libmatrix_sdk_ffi.so Section Breakdown

Why is mSzyfr's Matrix SDK 2.26× larger?
Debug symbol tables: .symtab (6.3 MB) + .strtab (18.7 MB) = 25.1 MB of debug symbols — absent from Element X's stripped build.
Larger .text section: 36.8 MB vs 23.2 MB (1.58× more executable code) — likely compiled with fewer optimizations or with debug assertions enabled.
Older Rust compiler: rustc 1.91.1 vs 1.93.1 — newer compiler may produce smaller code.
7,243 "debug" strings in mSzyfr vs 40 in Element X — strong indicator of a debug or development build profile.

3. DEX / Java+Kotlin Layer

DEX File Hashes

FilemSzyfrElementXMatch
classes.dex8.2 MB10.9 MBNO
classes2.dex7.2 MB283 KBNO
classes3.dex7.1 MB10.6 MBNO
DEX sizes differ drastically because mSzyfr uses R8 obfuscation (shrinks and renames classes) while Element X Play Store build does not. This makes direct class-name comparison unreliable.

Class Overlap Analysis

Class Overlap Detail

23,510
mSzyfr total classes
23,561
ElementX total classes
6,500
Identical class names
16.0%
Jaccard similarity
The 16% Jaccard similarity is misleading. mSzyfr has 12,882 obfuscated classes (short names like La/a;, Lb/c$a;) created by R8 which don't match Element X's full names. The 6,500 common classes are the ones R8 preserved — mostly from androidx.* (3,207), org.matrix.* (2,176), and io.element.* (78). The real code overlap is far higher than 16% — the R8 mapping file (not publicly available) would be needed to prove it precisely.

4. DEX String Analysis

String Overlap

mSzyfr-Unique Government Strings

Strings found only in mSzyfr that reveal NASK customization:

pl.nask.mszyfrApp package ID
Komunikator Narodowy AndroidApp internal name
komunikator.narodowy.gov.plGov domain
https://matrix.komunikator.narodowy.gov.pl/_matrix/push/v1/notify
Push notification gateway pointing to government Matrix homeserver
Only 8 strings are NASK-specific. Everything else is standard Element X / Matrix SDK content.

5. Bundled Assets

Asset File Comparison

Asset Breakdown

mSzyfr total assets1,042
ElementX total assets1,450
Common paths300
Byte-identical (of common)130 (43%)
Different (of common)170 (57%)
Only in mSzyfr742
Only in ElementX1,150
Most "different" common files are compiled Android XML resources — same content but different binary compilation. The 170 different files mostly have identical sizes, suggesting same source compiled by different AAPT2 versions.

Element Call Configuration Difference

mSzyfr config.json
{
  "matrix_rtc_session": {
    "delayed_leave_event_delay_ms": 90000,
    "delayed_leave_event_restart_local_timeout_ms": 10000,
    "delayed_leave_event_restart_ms": 4000,
    "membership_event_expiry_ms": 7200000,
    "network_error_retry_ms": 100,
    "wait_for_key_rotation_ms": 5000
  }
}
Element X config.json
{
  "matrix_rtc_session": {
    "wait_for_key_rotation_ms": 5000,
    "delayed_leave_event_restart_ms": 4000,
    "delayed_leave_event_delay_ms": 18000
  }
}
mSzyfr has more RTC session parameters and a 90-second delayed leave (vs 18 seconds) — suggesting a tuned deployment for government call infrastructure with longer session keepalive.

6. Architectural Differences

FeaturemSzyfrElement XImpact
Push notifications Firebase → matrix.komunikator.narodowy.gov.pl Firebase → matrix.org / UnifiedPush mSzyfr routes pushes through government gateway
Crash reporting (Sentry) REMOVED Present (libsentry.so) mSzyfr doesn't send crash data to Element's Sentry
Bug reporting (Rageshake) References to rageshakes.element.io in strings rageshakes.element.io May still report to Element unless server-side blocked
License check com.pairip.licensecheck.LicenseActivity Same Both use Google Play license verification
Element Call bundle Different JS bundle hashes, more config params Different JS bundle, fewer config params Different versions of Element Call bundled
Native symbol stripping UNSTRIPPED (413K symbols) STRIPPED (0 symbols) mSzyfr leaks all Rust internal function names
R8 / ProGuard ENABLED DISABLED mSzyfr obfuscates Java, ElementX does not

7. Security Observation

Paradoxical Security Posture: NASK applied R8 obfuscation to the Java/Kotlin layer (hiding 12,882 class names) — but left the native Rust library completely unstripped, exposing 413,457 internal symbol names totaling 25.1 MB. This means a reverse engineer can read every Rust function name in the Matrix SDK, including internal cryptographic routines, protocol handlers, and data structures. The Java obfuscation is cosmetic when the native layer is wide open. Element X (the consumer app) does the opposite — no Java obfuscation but a properly stripped native library.

Methodology & Limitations

This comparison uses SHA-256 hashes for byte-level identity, readelf for ELF section analysis, nm for symbol counting, and androguard for DEX class/string extraction. The 16% Jaccard class overlap is artificially low due to R8 obfuscation in mSzyfr — the true code overlap cannot be measured without NASK's ProGuard mapping file. Native library size differences are explained by debug symbols and compiler version differences. All data was extracted directly from the provided APK files, with no assumptions or interpolation.

Analysis by Bartosz Srokabartoszsroka.com