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
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
| Package | pl.nask.mszyfr |
| Version | 26.03.3 (code 202603030) |
| Developer | NASK (Polish Gov Research Institute) |
| Rust compiler | rustc 1.91.1 (2025-11-07) |
| Clang | 19.0.1 (Android r530567e) |
| Linker | LLD 19.0.1 |
| R8 obfuscation | YES — 12,882 obfuscated classes |
| Native symbols | UNSTRIPPED — 413K symbols |
Element X v26.03.2
| Package | io.element.android.x |
| Version | 26.03.2 (code 202603021) |
| Developer | Element (New Vector Ltd) |
| Rust compiler | rustc 1.93.1 (2026-02-11) |
| Clang | 21.0.0 (Android r563880c) |
| Linker | LLD 21.0.0 |
| R8 obfuscation | NO — 0 obfuscated classes |
| Native symbols | STRIPPED — 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
| Library | mSzyfr Size | ElementX Size | Verdict |
libandroidx.graphics.path.so | 7,252 B | 7,252 B | IDENTICAL |
libdatastore_shared_counter.so | 53,412 B | 53,412 B | IDENTICAL |
libimage_processing_util_jni.so | 24,448 B | 24,448 B | IDENTICAL |
libjnidispatch.so | 126,496 B | 126,496 B | IDENTICAL |
libopuscodec.so | 506,040 B | 506,040 B | IDENTICAL |
libsurface_util_jni.so | 3,460 B | 3,460 B | IDENTICAL |
libmatrix_sdk_ffi.so | 74.7 MB | 32.9 MB | DIFFERENT (2.26×) |
libmaplibre.so | 9.6 MB | 9.6 MB | DIFFERENT |
libsqlcipher.so | 4.1 MB | 4.1 MB | DIFFERENT |
libuniffi_wysiwyg_composer.so | 2.2 MB | 2.2 MB | DIFFERENT |
libzxingcpp_android.so | 1.2 MB | 1.2 MB | DIFFERENT |
libsentry.so | — | 483 KB | MISSING from mSzyfr |
libsentry-android.so | — | 12 KB | MISSING 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
| File | mSzyfr | ElementX | Match |
| classes.dex | 8.2 MB | 10.9 MB | NO |
| classes2.dex | 7.2 MB | 283 KB | NO |
| classes3.dex | 7.1 MB | 10.6 MB | NO |
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 Detail
23,510
mSzyfr total classes
23,561
ElementX total classes
6,500
Identical class names
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
mSzyfr-Unique Government Strings
Strings found only in mSzyfr that reveal NASK customization:
pl.nask.mszyfr | App package ID |
Komunikator Narodowy Android | App internal name |
komunikator.narodowy.gov.pl | Gov 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 Breakdown
| mSzyfr total assets | 1,042 |
| ElementX total assets | 1,450 |
| Common paths | 300 |
| Byte-identical (of common) | 130 (43%) |
| Different (of common) | 170 (57%) |
| Only in mSzyfr | 742 |
| Only in ElementX | 1,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
| Feature | mSzyfr | Element X | Impact |
| 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.