Wallet Logo

Mycelium Bitcoin Wallet

Latest release: 3.16.0.13 ( 13th September 2022 ) πŸ” Last analysed 4th November 2022 . Not reproducible from source provided
4.1 β˜…β˜…β˜…β˜…β˜…
11650 ratings
1 million
1st July 2013

We could not verify that the provided code matches the binary!

As part of our Methodology, we ask:

Is the published binary matching the published source code?

If the answer is "no", we mark it as "Not reproducible from source provided".

Published code doesn’t help much if it is not what the published binary was built from. That is why we try to reproduce the binary. We

  1. obtain the binary from the provider
  2. compile the published source code using the published build instructions into a binary
  3. compare the two binaries
  4. we might spend some time working around issues that are easy to work around

If this fails, we might search if other revisions match or if we can deduct the source of the mismatch but generally consider it on the provider to provide the correct source code and build instructions to reproduce the build, so we usually open a ticket in their code repository.

In any case, the result is a discrepancy between the binary we can create and the binary we can find for download and any discrepancy might leak your backup to the server on purpose or by accident.

As we cannot verify that the source provided is the source the binary was compiled from, this category is only slightly better than closed source but for now we have hope projects come around and fix verifiability issues.

The product cannot be independently verified. If the provider puts your funds at risk on purpose or by accident, you will probably not know about the issue before people start losing money. If the provider is more criminally inclined he might have collected all the backups of all the wallets, ready to be emptied at the press of a button. The product might have a formidable track record but out of distress or change in management turns out to be evil from some point on, with nobody outside ever knowing before it is too late.

Show Older Reviews

Help spread awareness for build reproducibility

Please help us spread the word, asking Mycelium Bitcoin Wallet to support reproducible builds  via their Twitter!

Do your own research!

Try out searching for "lost bitcoins", "stole my money" or "scammers" together with the wallet's name, even if you think the wallet is generally trustworthy. For all the bigger wallets you will find accusations. Make sure you understand why they were made and if you are comfortable with the provider's reaction.

If you find something we should include, you can create an issue or edit this analysis yourself and create a merge request for your changes.

The Analysis 

Update 2022-11-04: After the provider had failed to publish the source code resulting in a No Source! on 2022-11-01, the source was released today and we ran our test script again:

===== Begin Results =====
appId:          com.mycelium.wallet
signer:         b8e59d4a60b65290efb2716319e50b94e298d7a72c76c2119eb7d8d3afac302e
apkVersionName: 3.16.0.13
apkVersionCode: 3160013
verdict:        
appHash:        76a431f30c9257c478c6d1072aeaaaddc4807f2a9f9379791c768866ec4bdec7
commit:         1544c49cdde85dc060875ae601415016537f5419

Diff:
Files /tmp/fromPlay_com.mycelium.wallet_3160013/classes3.dex and /tmp/fromBuild_com.mycelium.wallet_3160013/classes3.dex differ
Files /tmp/fromPlay_com.mycelium.wallet_3160013/META-INF/CERT.RSA and /tmp/fromBuild_com.mycelium.wallet_3160013/META-INF/CERT.RSA differ
Files /tmp/fromPlay_com.mycelium.wallet_3160013/META-INF/CERT.SF and /tmp/fromBuild_com.mycelium.wallet_3160013/META-INF/CERT.SF differ
Files /tmp/fromPlay_com.mycelium.wallet_3160013/META-INF/MANIFEST.MF and /tmp/fromBuild_com.mycelium.wallet_3160013/META-INF/MANIFEST.MF differ

Revision, tag (and its signature):

===== End Results =====

With a diff in classes3.dex this product is not verifiable from its source code.

The diffoscope output for classes3.dex:

β”œβ”€β”€ classes3.dex
β”‚ β”œβ”€β”€ classes3.jar
β”‚ β”‚ β”œβ”€β”€ zipinfo {}
β”‚ β”‚ β”‚ @@ -1,8 +1,8 @@
β”‚ β”‚ β”‚ -Zip file size: 16107502 bytes, number of entries: 7492
β”‚ β”‚ β”‚ +Zip file size: 16107503 bytes, number of entries: 7492
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx      245 b- stor 80-Jan-01 00:00 com/mrd/bitlib/model/AddressType$Companion.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx     1087 b- stor 80-Jan-01 00:00 com/mrd/bitlib/model/AddressType.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx       81 b- stor 80-Jan-01 00:00 com/mrd/bitlib/model/Bech32$1.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx      366 b- stor 80-Jan-01 00:00 com/mrd/bitlib/model/Bech32$Bech32Data.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx      180 b- stor 80-Jan-01 00:00 com/mrd/bitlib/model/Bech32$Bech32Exception.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx     3870 b- stor 80-Jan-01 00:00 com/mrd/bitlib/model/Bech32.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx      649 b- stor 80-Jan-01 00:00 com/mrd/bitlib/model/BitcoinAddress$1.class
β”‚ β”‚ β”‚ @@ -7487,8 +7487,8 @@
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx      689 b- stor 80-Jan-01 00:00 com/mycelium/wallet/activity/receive/ReceiveCoinsViewModel$init$1.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx      698 b- stor 80-Jan-01 00:00 com/mycelium/wallet/activity/receive/ReceiveCoinsViewModel$isInitialized$1.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx      698 b- stor 80-Jan-01 00:00 com/mycelium/wallet/activity/send/model/SendCoinsViewModel$isInitialized$1.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx     1099 b- stor 80-Jan-01 00:00 com/mycelium/wapi/wallet/bch/bip44/Bip44BCHAccount.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx     1292 b- stor 80-Jan-01 00:00 com/mycelium/wapi/wallet/bch/single/SingleAddressBCHAccount.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx     5651 b- stor 80-Jan-01 00:00 com/mycelium/wapi/wallet/btc/bip44/HDAccountExternalSignature.class
β”‚ β”‚ β”‚  ?rwxrwxr-x  2.0 unx     1139 b- stor 80-Jan-01 00:00 com/mycelium/wapi/wallet/btc/bip44/HDPubOnlyAccount.class
β”‚ β”‚ β”‚ -7492 files, 17610442 bytes uncompressed, 14518228 bytes compressed:  17.6%
β”‚ β”‚ β”‚ +7492 files, 17610442 bytes uncompressed, 14518229 bytes compressed:  17.6%
β”‚ β”‚ β”œβ”€β”€ com/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl.class
β”‚ β”‚ β”‚ β”œβ”€β”€ procyon -ec {}
β”‚ β”‚ β”‚ β”‚ @@ -30,16 +30,16 @@
β”‚ β”‚ β”‚ β”‚  
β”‚ β”‚ β”‚ β”‚  public class FragmentChangelly2ExchangeBindingImpl extends FragmentChangelly2ExchangeBinding
β”‚ β”‚ β”‚ β”‚  {
β”‚ β”‚ β”‚ β”‚      private static final ViewDataBinding$IncludedLayouts sIncludes;
β”‚ β”‚ β”‚ β”‚      private static final SparseIntArray sViewsWithIds;
β”‚ β”‚ β”‚ β”‚      private ViewDataBinding$PropertyChangedInverseListener buyLayoutvalue;
β”‚ β”‚ β”‚ β”‚      private long mDirtyFlags;
β”‚ β”‚ β”‚ β”‚ -    private InverseBindingListener mOldEventValue2130743879;
β”‚ β”‚ β”‚ β”‚ -    private InverseBindingListener mOldEventValue880387685;
β”‚ β”‚ β”‚ β”‚ +    private InverseBindingListener mOldEventValue1158656703;
β”‚ β”‚ β”‚ β”‚ +    private InverseBindingListener mOldEventValue881725763;
β”‚ β”‚ β”‚ β”‚      private final FrameLayout mboundView0;
β”‚ β”‚ β”‚ β”‚      private final ConstraintLayout mboundView1;
β”‚ β”‚ β”‚ β”‚      private final TextView mboundView7;
β”‚ β”‚ β”‚ β”‚      private ViewDataBinding$PropertyChangedInverseListener sellLayoutvalue;
β”‚ β”‚ β”‚ β”‚      
β”‚ β”‚ β”‚ β”‚      static {
β”‚ β”‚ β”‚ β”‚          final ViewDataBinding$IncludedLayouts viewDataBinding$IncludedLayouts = sIncludes = new ViewDataBinding$IncludedLayouts(18);
β”‚ β”‚ β”‚ β”‚ @@ -1067,17 +1067,17 @@
β”‚ β”‚ β”‚ β”‚              }
β”‚ β”‚ β”‚ β”‚              if ((mDirtyFlags & n3) != n30) {
β”‚ β”‚ β”‚ β”‚                  this.buyLayout.setFiatValue(fiatValue2);
β”‚ β”‚ β”‚ β”‚              }
β”‚ β”‚ β”‚ β”‚              final long n32 = 0x4000000L & mDirtyFlags;
β”‚ β”‚ β”‚ β”‚              if (n32 != n30) {
β”‚ β”‚ β”‚ β”‚                  this.buyLayout.setPartLabel(this.getRoot().getResources().getString(2131951799));
β”‚ β”‚ β”‚ β”‚ -                setBindingInverseListener((ViewDataBinding)this.buyLayout, this.mOldEventValue2130743879, this.buyLayoutvalue);
β”‚ β”‚ β”‚ β”‚ +                setBindingInverseListener((ViewDataBinding)this.buyLayout, this.mOldEventValue1158656703, this.buyLayoutvalue);
β”‚ β”‚ β”‚ β”‚                  this.sellLayout.setPartLabel(this.getRoot().getResources().getString(2131951800));
β”‚ β”‚ β”‚ β”‚ -                setBindingInverseListener((ViewDataBinding)this.sellLayout, this.mOldEventValue880387685, this.sellLayoutvalue);
β”‚ β”‚ β”‚ β”‚ +                setBindingInverseListener((ViewDataBinding)this.sellLayout, this.mOldEventValue881725763, this.sellLayoutvalue);
β”‚ β”‚ β”‚ β”‚              }
β”‚ β”‚ β”‚ β”‚              final long n33 = 0x5004000L & mDirtyFlags;
β”‚ β”‚ β”‚ β”‚              final long n34 = 0L;
β”‚ β”‚ β”‚ β”‚              if (n33 != n34) {
β”‚ β”‚ β”‚ β”‚                  this.buyLayout.setValue(value2);
β”‚ β”‚ β”‚ β”‚              }
β”‚ β”‚ β”‚ β”‚              if ((0x5002000L & mDirtyFlags) != n34) {
β”‚ β”‚ β”‚ β”‚ @@ -1128,16 +1128,16 @@
β”‚ β”‚ β”‚ β”‚              if ((mDirtyFlags & 0x5800000L) != n36) {
β”‚ β”‚ β”‚ β”‚                  this.swapAccount.setEnabled(enabled);
β”‚ β”‚ β”‚ β”‚                  if (getBuildSdkInt() >= 11) {
β”‚ β”‚ β”‚ β”‚                      this.swapAccount.setAlpha(alpha);
β”‚ β”‚ β”‚ β”‚                  }
β”‚ β”‚ β”‚ β”‚              }
β”‚ β”‚ β”‚ β”‚              if (n32 != 0L) {
β”‚ β”‚ β”‚ β”‚ -                this.mOldEventValue2130743879 = (InverseBindingListener)this.buyLayoutvalue;
β”‚ β”‚ β”‚ β”‚ -                this.mOldEventValue880387685 = (InverseBindingListener)this.sellLayoutvalue;
β”‚ β”‚ β”‚ β”‚ +                this.mOldEventValue1158656703 = (InverseBindingListener)this.buyLayoutvalue;
β”‚ β”‚ β”‚ β”‚ +                this.mOldEventValue881725763 = (InverseBindingListener)this.sellLayoutvalue;
β”‚ β”‚ β”‚ β”‚              }
β”‚ β”‚ β”‚ β”‚              executeBindingsOn((ViewDataBinding)this.sellLayout);
β”‚ β”‚ β”‚ β”‚              executeBindingsOn((ViewDataBinding)this.buyLayout);
β”‚ β”‚ β”‚ β”‚          }
β”‚ β”‚ β”‚ β”‚      }
β”‚ β”‚ β”‚ β”‚      
β”‚ β”‚ β”‚ β”‚      public boolean hasPendingBindings() {

...

β”œβ”€β”€ smali_classes3/com/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl.smali
β”‚ @@ -10,17 +10,17 @@
β”‚  
β”‚  
β”‚  # instance fields
β”‚  .field private buyLayoutvalue:Landroidx/databinding/ViewDataBinding$PropertyChangedInverseListener;
β”‚  
β”‚  .field private mDirtyFlags:J
β”‚  
β”‚ -.field private mOldEventValue2130743879:Landroidx/databinding/InverseBindingListener;
β”‚ +.field private mOldEventValue1158656703:Landroidx/databinding/InverseBindingListener;
β”‚  
β”‚ -.field private mOldEventValue880387685:Landroidx/databinding/InverseBindingListener;
β”‚ +.field private mOldEventValue881725763:Landroidx/databinding/InverseBindingListener;
β”‚  
β”‚  .field private final mboundView0:Landroid/widget/FrameLayout;
β”‚  
β”‚  .field private final mboundView1:Landroidx/constraintlayout/widget/ConstraintLayout;
β”‚  
β”‚  .field private final mboundView7:Landroid/widget/TextView;
β”‚  
β”‚ @@ -3076,15 +3076,15 @@
β”‚      move-result-object v10
β”‚  
β”‚      invoke-virtual {v4, v10}, Lcom/mycelium/wallet/databinding/LayoutChangelly2CoinValueBinding;->setPartLabel(Ljava/lang/String;)V
β”‚  
β”‚      .line 989
β”‚      iget-object v4, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->buyLayout:Lcom/mycelium/wallet/databinding/LayoutChangelly2CoinValueBinding;
β”‚  
β”‚ -    iget-object v10, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->mOldEventValue2130743879:Landroidx/databinding/InverseBindingListener;
β”‚ +    iget-object v10, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->mOldEventValue1158656703:Landroidx/databinding/InverseBindingListener;
β”‚  
β”‚      iget-object v11, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->buyLayoutvalue:Landroidx/databinding/ViewDataBinding$PropertyChangedInverseListener;
β”‚  
β”‚      invoke-static {v4, v10, v11}, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->setBindingInverseListener(Landroidx/databinding/ViewDataBinding;Landroidx/databinding/InverseBindingListener;Landroidx/databinding/ViewDataBinding$PropertyChangedInverseListener;)V
β”‚  
β”‚      .line 990
β”‚      iget-object v4, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->sellLayout:Lcom/mycelium/wallet/databinding/LayoutChangelly2CoinValueBinding;
β”‚ @@ -3104,15 +3104,15 @@
β”‚      move-result-object v10
β”‚  
β”‚      invoke-virtual {v4, v10}, Lcom/mycelium/wallet/databinding/LayoutChangelly2CoinValueBinding;->setPartLabel(Ljava/lang/String;)V
β”‚  
β”‚      .line 991
β”‚      iget-object v4, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->sellLayout:Lcom/mycelium/wallet/databinding/LayoutChangelly2CoinValueBinding;
β”‚  
β”‚ -    iget-object v10, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->mOldEventValue880387685:Landroidx/databinding/InverseBindingListener;
β”‚ +    iget-object v10, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->mOldEventValue881725763:Landroidx/databinding/InverseBindingListener;
β”‚  
β”‚      iget-object v11, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->sellLayoutvalue:Landroidx/databinding/ViewDataBinding$PropertyChangedInverseListener;
β”‚  
β”‚      invoke-static {v4, v10, v11}, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->setBindingInverseListener(Landroidx/databinding/ViewDataBinding;Landroidx/databinding/InverseBindingListener;Landroidx/databinding/ViewDataBinding$PropertyChangedInverseListener;)V
β”‚  
β”‚      :cond_50
β”‚      const-wide/32 v10, 0x5004000
β”‚ @@ -3384,20 +3384,20 @@
β”‚      cmp-long v0, v7, v2
β”‚  
β”‚      if-eqz v0, :cond_60
β”‚  
β”‚      .line 1078
β”‚      iget-object v0, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->buyLayoutvalue:Landroidx/databinding/ViewDataBinding$PropertyChangedInverseListener;
β”‚  
β”‚ -    iput-object v0, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->mOldEventValue2130743879:Landroidx/databinding/InverseBindingListener;
β”‚ +    iput-object v0, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->mOldEventValue1158656703:Landroidx/databinding/InverseBindingListener;
β”‚  
β”‚      .line 1079
β”‚      iget-object v0, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->sellLayoutvalue:Landroidx/databinding/ViewDataBinding$PropertyChangedInverseListener;
β”‚  
β”‚ -    iput-object v0, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->mOldEventValue880387685:Landroidx/databinding/InverseBindingListener;
β”‚ +    iput-object v0, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->mOldEventValue881725763:Landroidx/databinding/InverseBindingListener;
β”‚  
β”‚      .line 1081
β”‚      :cond_60
β”‚      iget-object v0, v1, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->sellLayout:Lcom/mycelium/wallet/databinding/LayoutChangelly2CoinValueBinding;
β”‚  
β”‚      invoke-static {v0}, Lcom/mycelium/wallet/databinding/FragmentChangelly2ExchangeBindingImpl;->executeBindingsOn(Landroidx/databinding/ViewDataBinding;)V

The diff looks benign. Two geenrated variable names differ between the two binaries.

Disclaimer: Authors of this project have contributed to Mycelium.

Independent re-builds:

Here we test if the latest version also can be reproduced, following the known procedure expressed in our test script (?):

+ git clone --quiet --branch v3.16.0.13 --depth 1 https://github.com/mycelium-com/wallet-android app
warning: Could not find remote branch v3.16.0.13 to clone.
fatal: Remote branch v3.16.0.13 not found in upstream origin
+ exit 1

This is not good. The current version on Play Store is v3.16.0.13 yet the latest version on their public repository is 3.16.0.8 authored on 2022-07-27, more than three months ago!

This wallet is not verifiable!

(lw)