Refactor key handling based on security audit

pull/785/head
James Brown 5 years ago
parent 25f1e96ca6
commit 1efced680b
  1. 12
      app/src/main/java/io/stormbird/wallet/di/TokenFunctionModule.java
  2. 4
      app/src/main/java/io/stormbird/wallet/entity/BackupTokenCallback.java
  3. 1
      app/src/main/java/io/stormbird/wallet/entity/CryptoFunctions.java
  4. 32
      app/src/main/java/io/stormbird/wallet/entity/Wallet.java
  5. 4
      app/src/main/java/io/stormbird/wallet/interact/FetchWalletsInteract.java
  6. 3
      app/src/main/java/io/stormbird/wallet/repository/TransactionRepository.java
  7. 364
      app/src/main/java/io/stormbird/wallet/repository/WalletDataRealmSource.java
  8. 6
      app/src/main/java/io/stormbird/wallet/repository/WalletRepository.java
  9. 1
      app/src/main/java/io/stormbird/wallet/repository/WalletRepositoryType.java
  10. 60
      app/src/main/java/io/stormbird/wallet/repository/entity/RealmKeyType.java
  11. 25
      app/src/main/java/io/stormbird/wallet/repository/entity/RealmWalletData.java
  12. 11
      app/src/main/java/io/stormbird/wallet/service/FeeMasterService.java
  13. 13
      app/src/main/java/io/stormbird/wallet/service/ImportTokenService.java
  14. 658
      app/src/main/java/io/stormbird/wallet/service/KeyService.java
  15. 29
      app/src/main/java/io/stormbird/wallet/service/KeystoreAccountService.java
  16. 16
      app/src/main/java/io/stormbird/wallet/service/MarketQueueService.java
  17. 4
      app/src/main/java/io/stormbird/wallet/service/RealmManager.java
  18. 75
      app/src/main/java/io/stormbird/wallet/ui/BackupKeyActivity.java
  19. 4
      app/src/main/java/io/stormbird/wallet/ui/DappBrowserFragment.java
  20. 11
      app/src/main/java/io/stormbird/wallet/ui/FunctionActivity.java
  21. 11
      app/src/main/java/io/stormbird/wallet/ui/HomeActivity.java
  22. 15
      app/src/main/java/io/stormbird/wallet/ui/ImportWalletActivity.java
  23. 4
      app/src/main/java/io/stormbird/wallet/ui/NewSettingsFragment.java
  24. 1
      app/src/main/java/io/stormbird/wallet/ui/RedeemSignatureDisplayActivity.java
  25. 5
      app/src/main/java/io/stormbird/wallet/ui/WalletActionsActivity.java
  26. 15
      app/src/main/java/io/stormbird/wallet/ui/WalletFragment.java
  27. 20
      app/src/main/java/io/stormbird/wallet/ui/WalletsActivity.java
  28. 3
      app/src/main/java/io/stormbird/wallet/ui/widget/entity/WarningData.java
  29. 4
      app/src/main/java/io/stormbird/wallet/ui/widget/holder/WarningHolder.java
  30. 30
      app/src/main/java/io/stormbird/wallet/viewmodel/BackupKeyViewModel.java
  31. 2
      app/src/main/java/io/stormbird/wallet/viewmodel/ConfirmationViewModel.java
  32. 4
      app/src/main/java/io/stormbird/wallet/viewmodel/DappBrowserViewModel.java
  33. 2
      app/src/main/java/io/stormbird/wallet/viewmodel/ImportTokenViewModel.java
  34. 8
      app/src/main/java/io/stormbird/wallet/viewmodel/ImportWalletViewModel.java
  35. 2
      app/src/main/java/io/stormbird/wallet/viewmodel/RedeemSignatureDisplayModel.java
  36. 2
      app/src/main/java/io/stormbird/wallet/viewmodel/SellDetailViewModel.java
  37. 32
      app/src/main/java/io/stormbird/wallet/viewmodel/TokenFunctionViewModel.java
  38. 8
      app/src/main/java/io/stormbird/wallet/viewmodel/TokenFunctionViewModelFactory.java
  39. 2
      app/src/main/java/io/stormbird/wallet/viewmodel/TransferTicketDetailViewModel.java
  40. 5
      app/src/main/java/io/stormbird/wallet/viewmodel/WalletViewModel.java
  41. 12
      app/src/main/java/io/stormbird/wallet/viewmodel/WalletsViewModel.java
  42. 1
      app/src/main/res/values-es/strings.xml
  43. 1
      app/src/main/res/values-zh/strings.xml
  44. 1
      app/src/main/res/values/strings.xml
  45. 2
      app/src/test/java/io/stormbird/wallet/AssetGenerationTest.java
  46. 5
      app/src/test/java/io/stormbird/wallet/CryptoFunctions.java

@ -4,9 +4,11 @@ import dagger.Module;
import dagger.Provides;
import io.stormbird.wallet.interact.CreateTransactionInteract;
import io.stormbird.wallet.interact.FetchTokensInteract;
import io.stormbird.wallet.interact.GenericWalletInteract;
import io.stormbird.wallet.repository.EthereumNetworkRepositoryType;
import io.stormbird.wallet.repository.TokenRepositoryType;
import io.stormbird.wallet.repository.TransactionRepositoryType;
import io.stormbird.wallet.repository.WalletRepositoryType;
import io.stormbird.wallet.router.SellTicketRouter;
import io.stormbird.wallet.router.TransferTicketDetailRouter;
import io.stormbird.wallet.service.AssetDefinitionService;
@ -31,10 +33,11 @@ public class TokenFunctionModule
GasService gasService,
TokensService tokensService,
EthereumNetworkRepositoryType ethereumNetworkRepository,
KeyService keyService) {
KeyService keyService,
GenericWalletInteract genericWalletInteract) {
return new TokenFunctionViewModelFactory(
assetDefinitionService, sellTicketRouter, transferTicketRouter, createTransactionInteract, gasService, tokensService, ethereumNetworkRepository, keyService);
assetDefinitionService, sellTicketRouter, transferTicketRouter, createTransactionInteract, gasService, tokensService, ethereumNetworkRepository, keyService, genericWalletInteract);
}
@Provides
@ -56,4 +59,9 @@ public class TokenFunctionModule
FetchTokensInteract provideFetchTokensInteract(TokenRepositoryType tokenRepository) {
return new FetchTokensInteract(tokenRepository);
}
@Provides
GenericWalletInteract provideGenericWalletInteract(WalletRepositoryType walletRepository) {
return new GenericWalletInteract(walletRepository);
}
}

@ -6,6 +6,6 @@ package io.stormbird.wallet.entity;
*/
public interface BackupTokenCallback
{
void BackupClick(String address);
void remindMeLater(String address);
void BackupClick(Wallet wallet);
void remindMeLater(Wallet wallet);
}

@ -28,6 +28,7 @@ public class CryptoFunctions implements CryptoFunctionsInterface
public BigInteger signedMessageToKey(byte[] data, byte[] signature) throws SignatureException
{
Sign.SignatureData sigData = sigFromByteArray(signature);
if (sigData == null) return BigInteger.ZERO;
return Sign.signedMessageToKey(data, sigData);
}

@ -20,6 +20,7 @@ public class Wallet implements Parcelable {
public WalletType type;
public long lastBackupTime;
public KeyService.AuthenticationLevel authLevel;
public long walletCreationTime;
public Wallet(String address) {
this.address = address;
@ -29,6 +30,7 @@ public class Wallet implements Parcelable {
this.type = WalletType.NOT_DEFINED;
this.lastBackupTime = 0;
this.authLevel = KeyService.AuthenticationLevel.NOT_SET;
this.walletCreationTime = 0;
}
private Wallet(Parcel in)
@ -42,6 +44,7 @@ public class Wallet implements Parcelable {
lastBackupTime = in.readLong();
t = in.readInt();
authLevel = KeyService.AuthenticationLevel.values()[t];
walletCreationTime = in.readLong();
}
public void setWalletType(WalletType wType)
@ -49,34 +52,6 @@ public class Wallet implements Parcelable {
type = wType;
}
public void checkWalletType(Context ctx)
{
type = getKeystoreType(ctx, address);
authLevel = getAuthLevel(ctx, address);
}
public static WalletType getKeystoreType(Context context, String addr)
{
if (addr == null) return WalletType.NOT_DEFINED;
else if (new File(context.getFilesDir(), addr+HDKEY_LABEL).exists() ||
new File(context.getFilesDir(), addr+NO_AUTH_LABEL+HDKEY_LABEL).exists()) return WalletType.HDKEY;
else if (new File(context.getFilesDir(), addr + KEYSTORE_LABEL).exists()
|| new File(context.getFilesDir(), addr + NO_AUTH_LABEL + KEYSTORE_LABEL).exists()) return WalletType.KEYSTORE;
else if (new File(context.getFilesDir(), addr).exists()) return WalletType.KEYSTORE_LEGACY;
else return WalletType.WATCH;
}
private static AuthenticationLevel getAuthLevel(Context context, String addr)
{
if (new File(context.getFilesDir(), addr+HDKEY_LABEL).exists() ||
new File(context.getFilesDir(), addr+KEYSTORE_LABEL).exists())
return KeyService.hasStrongbox() ? AuthenticationLevel.STRONGBOX_AUTHENTICATION : AuthenticationLevel.TEE_AUTHENTICATION;
else if (new File(context.getFilesDir(), addr + NO_AUTH_LABEL + HDKEY_LABEL).exists()
|| new File(context.getFilesDir(), addr + NO_AUTH_LABEL + KEYSTORE_LABEL).exists()
|| new File(context.getFilesDir(), addr).exists())
return KeyService.hasStrongbox() ? AuthenticationLevel.STRONGBOX_NO_AUTHENTICATION : AuthenticationLevel.TEE_NO_AUTHENTICATION;
else return AuthenticationLevel.NOT_SET;
}
public static final Creator<Wallet> CREATOR = new Creator<Wallet>() {
@Override
public Wallet createFromParcel(Parcel in) {
@ -108,6 +83,7 @@ public class Wallet implements Parcelable {
parcel.writeInt(type.ordinal());
parcel.writeLong(lastBackupTime);
parcel.writeInt(authLevel.ordinal());
parcel.writeLong(walletCreationTime);
}
public void setWalletBalance(BigDecimal balanceBD)

@ -45,6 +45,10 @@ public class FetchWalletsInteract {
return accountRepository.storeWallet(wallet);
}
public Single<Wallet> updateWalletData(Wallet wallet) {
return accountRepository.updateWalletData(wallet);
}
/**
* Called when wallet marked as backed up.
* Update the wallet backup date

@ -25,7 +25,7 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import static io.stormbird.wallet.service.MarketQueueService.sigFromByteArray;
import static io.stormbird.wallet.entity.CryptoFunctions.sigFromByteArray;
public class TransactionRepository implements TransactionRepositoryType {
@ -168,6 +168,7 @@ public class TransactionRepository implements TransactionRepositoryType {
{
return Single.fromCallable(() -> {
Sign.SignatureData sigData = sigFromByteArray(signatureBytes);
if (sigData == null) return "0000".getBytes();
return encode(rtx, sigData);
});
}

@ -13,6 +13,7 @@ import io.realm.RealmResults;
import io.realm.Sort;
import io.stormbird.wallet.entity.Wallet;
import io.stormbird.wallet.entity.WalletType;
import io.stormbird.wallet.repository.entity.RealmKeyType;
import io.stormbird.wallet.repository.entity.RealmWalletData;
import io.stormbird.wallet.service.KeyService;
import io.stormbird.wallet.service.RealmManager;
@ -32,46 +33,60 @@ public class WalletDataRealmSource {
public Single<Wallet[]> populateWalletData(Wallet[] wallets, KeyService keyService) {
return Single.fromCallable(() -> {
List<Wallet> walletList = new ArrayList<>();
for (Wallet w : wallets) if (w != null && w.address != null) { w.type = WalletType.KEYSTORE; };
List<Wallet> walletList = loadOrCreateKeyRealmDB(wallets, keyService); //call has action on upgrade to new UX
try (Realm realm = realmManager.getWalletDataRealmInstance())
{
for (Wallet hdWallet : keyService.getAllHDWallets())
//Add additional - non critical wallet data. This database can be voided for upgrade if required
for (Wallet wallet : walletList)
{
RealmWalletData data = realm.where(RealmWalletData.class)
.equalTo("address", hdWallet.address)
.equalTo("address", wallet.address)
.findFirst();
composeWallet(hdWallet, data);
walletList.add(hdWallet);
composeWallet(wallet, data);
}
}
for (Wallet keyStoreWallet : wallets)
{
RealmWalletData data = realm.where(RealmWalletData.class)
.equalTo("address", keyStoreWallet.address)
.findFirst();
keyService.checkWalletType(keyStoreWallet);
composeWallet(keyStoreWallet, data);
walletList.add(keyStoreWallet);
}
return walletList.toArray(new Wallet[0]);
});
}
//finally add watch wallets
RealmResults<RealmWalletData> realmItems = realm.where(RealmWalletData.class)
.sort("lastBackup", Sort.ASCENDING)
.equalTo("type", WalletType.WATCH.ordinal())
.findAll();
private List<Wallet> loadOrCreateKeyRealmDB(Wallet[] wallets,KeyService keyService)
{
List<Wallet> walletList = new ArrayList<>();
try (Realm realm = realmManager.getWalletTypeRealmInstance())
{
RealmResults<RealmKeyType> realmKeyTypes = realm.where(RealmKeyType.class)
.sort("dateAdded", Sort.ASCENDING)
.findAll();
for (RealmWalletData walletData : realmItems)
if (realmKeyTypes.size() > 0)
{
//Load fixed wallet data: wallet type, creation and backup times
for (RealmKeyType walletTypeData : realmKeyTypes)
{
walletList.add(convertWallet(walletData));
Wallet w = composeKeyType(walletTypeData);
if (w != null) walletList.add(w);
}
}
return walletList.toArray(new Wallet[0]);
});
else //only zero on upgrade from v2.01.3 and lower (pre-HD key)
{
realm.beginTransaction();
for (Wallet wallet : wallets)
{
RealmKeyType realmKey = realm.createObject(RealmKeyType.class, wallet.address);
wallet.authLevel = KeyService.AuthenticationLevel.TEE_NO_AUTHENTICATION;
wallet.type = WalletType.KEYSTORE_LEGACY;
realmKey.setType(wallet.type); //all keys are legacy
realmKey.setLastBackup(System.currentTimeMillis());
realmKey.setDateAdded(wallet.walletCreationTime);
realmKey.setAuthLevel(wallet.authLevel);
walletList.add(wallet);
}
realm.commitTransaction();
}
}
return walletList;
}
private void composeWallet(Wallet wallet, RealmWalletData d)
@ -81,10 +96,24 @@ public class WalletDataRealmSource {
wallet.ENSname = d.getENSName();
wallet.balance = balance(d);
wallet.name = d.getName();
wallet.lastBackupTime = d.getLastBackup();
}
}
private Wallet composeKeyType(RealmKeyType keyType)
{
Wallet wallet = null;
if (keyType != null)
{
wallet = new Wallet(keyType.getAddress());
wallet.type = keyType.getType();
wallet.walletCreationTime = keyType.getDateAdded();
wallet.lastBackupTime = keyType.getLastBackup();
wallet.authLevel = keyType.getAuthLevel();
}
return wallet;
}
private String balance(RealmWalletData data)
{
String value = data.getBalance();
@ -96,9 +125,6 @@ public class WalletDataRealmSource {
Wallet wallet = new Wallet(data.getAddress());
wallet.ENSname = data.getENSName();
wallet.balance = data.getBalance();
wallet.lastBackupTime = data.getLastBackup();
wallet.authLevel = data.getAuthLevel();
wallet.type = data.getType();
wallet.name = data.getName();
return wallet;
}
@ -119,7 +145,6 @@ public class WalletDataRealmSource {
realmWallet.setENSName(wallet.ENSname);
if (mainNet) realmWallet.setBalance(wallet.balance);
realmWallet.setName(wallet.name);
realmWallet.setType(wallet.type);
updated++;
} else {
if (mainNet && (realmWallet.getBalance() == null || !wallet.balance.equals(realmWallet.getENSName())))
@ -127,7 +152,6 @@ public class WalletDataRealmSource {
if (wallet.ENSname != null && (realmWallet.getENSName() == null || !wallet.ENSname.equals(realmWallet.getENSName())))
realmWallet.setENSName(wallet.ENSname);
realmWallet.setName(wallet.name);
realmWallet.setType(wallet.type);
updated++;
}
}
@ -141,29 +165,15 @@ public class WalletDataRealmSource {
public Single<Wallet> storeWallet(Wallet wallet) {
return Single.fromCallable(() -> {
try (Realm realm = realmManager.getWalletDataRealmInstance()) {
realm.beginTransaction();
RealmWalletData realmWallet = realm.where(RealmWalletData.class)
.equalTo("address", wallet.address)
.findFirst();
if (realmWallet == null) {
realmWallet = realm.createObject(RealmWalletData.class, wallet.address);
}
realmWallet.setName(wallet.name);
realmWallet.setType(wallet.type);
realmWallet.setENSName(wallet.ENSname);
realmWallet.setBalance(wallet.balance);
realmWallet.setType(wallet.type);
realmWallet.setLastBackup(wallet.lastBackupTime);
realmWallet.setAuthLevel(wallet.authLevel);
storeKeyData(wallet);
storeWalletData(wallet);
return wallet;
});
}
realm.commitTransaction();
} catch (Exception e) {
Log.e(TAG, "storeWallet: " + e.getMessage(), e);
}
public Single<Wallet> updateWalletData(Wallet wallet) {
return Single.fromCallable(() -> {
storeWalletData(wallet);
return wallet;
});
}
@ -193,15 +203,19 @@ public class WalletDataRealmSource {
public void onStart()
{
realm = realmManager.getWalletDataRealmInstance();
realm.beginTransaction();
if (isBackupTime)
{
setKeyBackupTime(walletAddr);
}
RealmWalletData realmWallet = realm.where(RealmWalletData.class)
.equalTo("address", walletAddr)
.findFirst();
if (realmWallet != null)
{
realm.beginTransaction();
//Always update warning time but only update backup time if a backup was made
if (isBackupTime) realmWallet.setLastBackup(System.currentTimeMillis());
realmWallet.setLastWarning(System.currentTimeMillis());
}
}
@ -234,135 +248,193 @@ public class WalletDataRealmSource {
return updateTimeInternal(walletAddr, false);
}
private Single<Long> getWalletTime(String walletAddr, boolean isBackupTime)
public Single<String> getWalletRequiresBackup(String walletAddr)
{
return Single.fromCallable(() -> {
long backupTime = 0L;
try (Realm realm = realmManager.getWalletDataRealmInstance()) {
RealmWalletData realmWallet = realm.where(RealmWalletData.class)
.equalTo("address", walletAddr)
.findFirst();
if (realmWallet != null)
{
if (isBackupTime) backupTime = realmWallet.getLastBackup();
else backupTime = realmWallet.getLastWarning();
}
} catch (Exception e) {
Log.e(TAG, "getLastBackup: " + e.getMessage(), e);
}
return backupTime;
boolean wasDismissed = isDismissedInSettings(walletAddr);
long backupTime = getKeyBackupTime(walletAddr);
if (!wasDismissed && backupTime == 0) return walletAddr;
else return "";
});
}
public Single<Boolean> wasWarningDismissed(String walletAddr)
public Single<Boolean> getWalletBackupWarning(String walletAddr)
{
return getWalletTime(walletAddr, false)
.map(time -> (time & 0x1) == 1);
return Single.fromCallable(() -> {
long backupTime = getKeyBackupTime(walletAddr);
long warningTime = getWalletWarningTime(walletAddr);
return requiresBackup(backupTime, warningTime);
});
}
public Single<String> getWalletRequiresBackup(String wallet)
public Single<String> deleteWallet(String walletAddr)
{
return Single.fromCallable(() -> {
String walletBackup = "";
try (Realm realm = realmManager.getWalletDataRealmInstance()) {
RealmWalletData data = realm.where(RealmWalletData.class)
.equalTo("address", wallet)
.findFirst();
if (data != null &&
!data.getIsDismissedInSettings() &&
data.getLastBackup() == 0)
{
walletBackup = wallet;
}
// This checks if there's an HD wallet that has never been backed up,
// and the warning for which hasn't been dismissed within the last dismiss period
// for (RealmWalletData data : realmItems) {
// if (data.getType() == WalletType.HDKEY &&
// !data.getIsDismissedInSettings() &&
// (data.getLastBackup() == 0 &&
// System.currentTimeMillis() > (data.getLastWarning() + HDKeyService.TIME_BETWEEN_BACKUP_MILLIS)))
// {
// walletBackup = data.getAddress();
// break;
// }
// }
} catch (Exception e) {
e.printStackTrace();
try (Realm realm = realmManager.getWalletDataRealmInstance())
{
RealmWalletData realmWallet = realm.where(RealmWalletData.class).equalTo("address", walletAddr).findFirst();
realm.beginTransaction();
if (realmWallet != null) realmWallet.deleteFromRealm();
realm.commitTransaction();
}
try (Realm realm = realmManager.getWalletTypeRealmInstance())
{
RealmKeyType realmKey = realm.where(RealmKeyType.class).equalTo("address", walletAddr).findFirst();
realm.beginTransaction();
if (realmKey != null) realmKey.deleteFromRealm();
realm.commitTransaction();
}
return walletBackup;
return walletAddr;
});
}
public Single<Boolean> getWalletBackupWarning(String walletAddr)
public Single<String> setIsDismissed(String walletAddr, boolean isDismissed)
{
return Single.fromCallable(() -> {
long backupTime = 0L;
long warningTime = 0L;
try (Realm realm = realmManager.getWalletDataRealmInstance()) {
RealmWalletData realmWallet = realm.where(RealmWalletData.class)
.equalTo("address", walletAddr)
.findFirst();
if (realmWallet != null)
{
backupTime = realmWallet.getLastBackup();
warningTime = realmWallet.getLastWarning();
realm.beginTransaction();
realmWallet.setIsDismissedInSettings(isDismissed);
realm.commitTransaction();
}
} catch (Exception e) {
Log.e(TAG, "getLastBackup: " + e.getMessage(), e);
}
return requiresBackup(backupTime, warningTime);
return walletAddr;
});
}
private Boolean requiresBackup(Long backupTime, Long warningTime)
private void storeKeyData(Wallet wallet)
{
boolean warningDismissed = false;
if (System.currentTimeMillis() < (warningTime + KeyService.TIME_BETWEEN_BACKUP_WARNING_MILLIS))
try (Realm realm = realmManager.getWalletTypeRealmInstance())
{
warningDismissed = true;
RealmKeyType realmKey = realm.where(RealmKeyType.class)
.equalTo("address", wallet.address)
.findFirst();
realm.beginTransaction();
if (realmKey == null) {
realmKey = realm.createObject(RealmKeyType.class, wallet.address);
realmKey.setDateAdded(System.currentTimeMillis());
}
else if (realmKey.getDateAdded() == 0) realmKey.setDateAdded(System.currentTimeMillis());
realmKey.setType(wallet.type);
realmKey.setLastBackup(wallet.lastBackupTime);
realmKey.setAuthLevel(wallet.authLevel);
realmKey.setKeyModulus("");
realm.commitTransaction();
}
catch (Exception e)
{
e.printStackTrace();
}
}
// wallet never backed up but backup warning may have been swiped away
return !warningDismissed && backupTime == 0;
private void storeWalletData(Wallet wallet)
{
try (Realm realm = realmManager.getWalletDataRealmInstance())
{
RealmWalletData realmWallet = realm.where(RealmWalletData.class)
.equalTo("address", wallet.address)
.findFirst();
realm.beginTransaction();
if (realmWallet == null) {
realmWallet = realm.createObject(RealmWalletData.class, wallet.address);
}
realmWallet.setName(wallet.name);
realmWallet.setENSName(wallet.ENSname);
realmWallet.setBalance(wallet.balance);
realm.commitTransaction();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public Single<String> deleteWallet(String walletAddr)
private void setKeyBackupTime(String walletAddr)
{
return Single.fromCallable(() -> {
try (Realm realm = realmManager.getWalletDataRealmInstance()) {
RealmWalletData realmWallet = realm.where(RealmWalletData.class)
.equalTo("address", walletAddr)
.findFirst();
try (Realm realm = realmManager.getWalletTypeRealmInstance()) {
RealmKeyType realmKey = realm.where(RealmKeyType.class)
.equalTo("address", walletAddr)
.findFirst();
if (realmWallet != null)
{
realm.beginTransaction();
realmWallet.deleteFromRealm();
realm.commitTransaction();
}
if (realmKey != null)
{
realm.beginTransaction();
realmKey.setLastBackup(System.currentTimeMillis());
realm.commitTransaction();
}
return walletAddr;
});
} catch (Exception e) {
e.printStackTrace();
}
}
public Single<String> setIsDismissed(String walletAddr, boolean isDismissed)
private boolean isDismissedInSettings(String wallet)
{
return Single.fromCallable(() -> {
try (Realm realm = realmManager.getWalletDataRealmInstance()) {
RealmWalletData realmWallet = realm.where(RealmWalletData.class)
.equalTo("address", walletAddr)
.findFirst();
try (Realm realm = realmManager.getWalletDataRealmInstance()) {
RealmWalletData data = realm.where(RealmWalletData.class)
.equalTo("address", wallet)
.findFirst();
return data != null && data.getIsDismissedInSettings();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
if (realmWallet != null)
{
realm.beginTransaction();
realmWallet.setIsDismissedInSettings(isDismissed);
realm.commitTransaction();
}
private long getWalletWarningTime(String walletAddr)
{
try (Realm realm = realmManager.getWalletDataRealmInstance()) {
RealmWalletData data = realm.where(RealmWalletData.class)
.equalTo("address", walletAddr)
.findFirst();
if (data != null)
{
return data.getLastWarning();
}
return walletAddr;
});
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
private long getKeyBackupTime(String walletAddr)
{
try (Realm realm = realmManager.getWalletTypeRealmInstance()) {
RealmKeyType realmKey = realm.where(RealmKeyType.class)
.equalTo("address", walletAddr)
.findFirst();
if (realmKey != null)
{
return realmKey.getLastBackup();
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
private Boolean requiresBackup(Long backupTime, Long warningTime)
{
boolean warningDismissed = false;
if (System.currentTimeMillis() < (warningTime + KeyService.TIME_BETWEEN_BACKUP_WARNING_MILLIS))
{
warningDismissed = true;
}
// wallet never backed up but backup warning may have been swiped away
return !warningDismissed && backupTime == 0;
}
}

@ -165,6 +165,12 @@ public class WalletRepository implements WalletRepositoryType
return walletDataRealmSource.storeWallet(wallet);
}
@Override
public Single<Wallet> updateWalletData(Wallet wallet)
{
return walletDataRealmSource.updateWalletData(wallet);
}
@Override
public Single<String> getName(String address)
{

@ -36,6 +36,7 @@ public interface WalletRepositoryType {
Single<Integer> storeWallets(Wallet[] wallets, boolean isMainNet);
Single<Wallet> storeWallet(Wallet wallet);
Single<Wallet> updateWalletData(Wallet wallet);
Single<String> getName(String address);

@ -0,0 +1,60 @@
package io.stormbird.wallet.repository.entity;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
import io.stormbird.wallet.entity.WalletType;
import io.stormbird.wallet.service.KeyService;
public class RealmKeyType extends RealmObject
{
@PrimaryKey
private String address;
private byte type;
private byte authLevel;
private long lastBackup;
private long dateAdded;
private String modulus; //Added for future possibility that we use HD key modulus, so DB doesn't need to be re-initialised
public String getAddress()
{
return address;
}
public void setAddress(String address)
{
this.address = address;
}
public WalletType getType() { return WalletType.values()[type]; }
public void setType(WalletType type) { this.type = (byte)type.ordinal(); }
public KeyService.AuthenticationLevel getAuthLevel() { return KeyService.AuthenticationLevel.values()[authLevel]; }
public void setAuthLevel(KeyService.AuthenticationLevel authLevel) { this.authLevel = (byte)authLevel.ordinal(); }
public long getLastBackup()
{
return lastBackup;
}
public void setLastBackup(long lastBackup)
{
this.lastBackup = lastBackup;
}
public long getDateAdded()
{
return dateAdded;
}
public void setDateAdded(long dateAdded)
{
this.dateAdded = dateAdded;
}
public String getKeyModulus()
{
return modulus;
}
public void setKeyModulus(String modulus)
{
this.modulus = modulus;
}
}

@ -18,10 +18,7 @@ public class RealmWalletData extends RealmObject
private String ENSName;
private String balance;
private String name;
private int type;
private long lastBackup;
private long lastWarning;
private int authLevel;
public String getAddress()
{
@ -61,18 +58,6 @@ public class RealmWalletData extends RealmObject
this.name = name;
}
public WalletType getType() { return WalletType.values()[type]; }
public void setType(WalletType type) { this.type = type.ordinal(); }
public long getLastBackup()
{
return lastBackup;
}
public void setLastBackup(long lastBackup)
{
this.lastBackup = lastBackup;
}
public long getLastWarning()
{
return lastWarning;
@ -83,14 +68,4 @@ public class RealmWalletData extends RealmObject
}
public boolean getIsDismissedInSettings() { return (lastWarning & 0x1) == 1; }
public void setIsDismissedInSettings(boolean isDismissed) { lastWarning = (lastWarning&DISMISS_WARNING_IN_SETTINGS_MASK) + (isDismissed ? 0x1 : 0x0); }
public KeyService.AuthenticationLevel getAuthLevel()
{
return KeyService.AuthenticationLevel.values()[authLevel];
}
public void setAuthLevel(KeyService.AuthenticationLevel authLevel)
{
this.authLevel = authLevel.ordinal();
}
}

@ -25,7 +25,7 @@ import okhttp3.RequestBody;
import static io.stormbird.token.tools.ParseMagicLink.currencyLink;
import static io.stormbird.token.tools.ParseMagicLink.spawnable;
import static io.stormbird.wallet.service.MarketQueueService.sigFromByteArray;
import static io.stormbird.wallet.entity.CryptoFunctions.sigFromByteArray;
public class FeeMasterService
{
@ -121,9 +121,12 @@ public class FeeMasterService
private void addSignature(Map<String, String> args, byte[] sig)
{
Sign.SignatureData sigData = sigFromByteArray(sig);
args.put("r", Numeric.toHexString(sigData.getR()));
args.put("s", Numeric.toHexString(sigData.getS()));
args.put("v", Integer.toHexString(sigData.getV()));
if (sigData != null)
{
args.put("r", Numeric.toHexString(sigData.getR()));
args.put("s", Numeric.toHexString(sigData.getS()));
args.put("v", Integer.toHexString(sigData.getV()));
}
}
private Single<Integer> sendFeemasterTransaction(String url, int networkId, String toAddress, long expiry, String indices, byte[] tradeSig) {

@ -36,17 +36,4 @@ public class ImportTokenService {
public Single<byte[]> sign(Wallet wallet, byte[] data, int chainId) {
return transactionRepository.getSignature(wallet, data, chainId);
}
public static Sign.SignatureData sigFromByteArray(byte[] sig)
{
byte subv = (byte)(sig[64] + 27);
byte[] subrRev = Arrays.copyOfRange(sig, 0, 32);
byte[] subsRev = Arrays.copyOfRange(sig, 32, 64);
BigInteger r = new BigInteger(1, subrRev);
BigInteger s = new BigInteger(1, subsRev);
return new Sign.SignatureData(subv, subrRev, subsRev);
}
}

@ -25,7 +25,7 @@ import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.*;
import static io.stormbird.wallet.service.MarketQueueService.sigFromByteArray;
import static io.stormbird.wallet.entity.CryptoFunctions.sigFromByteArray;
public class KeystoreAccountService implements AccountKeystoreService
{
@ -117,7 +117,8 @@ public class KeystoreAccountService implements AccountKeystoreService
}
/**
* TODO: Import private key to keystore
* Import private key to keystore
*
* @param privateKey
* @param newPassword
* @return
@ -142,7 +143,8 @@ public class KeystoreAccountService implements AccountKeystoreService
}
/**
* TODO: Delete file, check if corresponding key is deleted
* Delete 'geth' keystore file then ensure password encrypted bytes and keys in Android keystore
* are deleted
* @param address account address
* @param password account password
* @return
@ -167,7 +169,7 @@ public class KeystoreAccountService implements AccountKeystoreService
}
//Now delete all traces of the key in Android keystore, encrypted bytes and iv file in private data area
keyService.deleteKeyCompletely(address);
keyService.deleteKey(address);
} );
}
@ -217,9 +219,10 @@ public class KeystoreAccountService implements AccountKeystoreService
);
byte[] signData = TransactionEncoder.encode(rtx);
byte[] signatureBytes = keyService.signData(signer.address, signData);
byte[] signatureBytes = keyService.signData(signer, signData);
Sign.SignatureData sigData = sigFromByteArray(signatureBytes);
return encode(rtx, sigData);
if (sigData == null) return "0000".getBytes();
else return encode(rtx, sigData);
})
.subscribeOn(Schedulers.io());
}
@ -314,7 +317,7 @@ public class KeystoreAccountService implements AccountKeystoreService
{
return Single.fromCallable(() -> {
//byte[] messageHash = Hash.sha3(message);
byte[] signed = keyService.signData(signer.address, message);
byte[] signed = keyService.signData(signer, message);
signed = patchSignatureVComponent(signed);
return signed;
@ -369,6 +372,7 @@ public class KeystoreAccountService implements AccountKeystoreService
String address = walletMap.get(d);
Wallet wallet = new Wallet(address);
wallet.type = WalletType.KEYSTORE;
wallet.walletCreationTime = d.getTime();
wallets.add(wallet);
}
@ -377,6 +381,17 @@ public class KeystoreAccountService implements AccountKeystoreService
.subscribeOn(Schedulers.io());
}
/**
* Patch the 'V'/position component of the signature bytes. The spongy castle signing algorithm returns
* 0 or 1 for the V component, and although most services accept this some require V to be 27 or 28
* This just changes 0 or 1 to 0x1b or 0x1c to be universally compatible with all ethereum services.
* Simple test example: login to 'Chibi Fighters' Dapp (in the discover dapps in the wallet)
* by signing their challenge. With V = 0 or 1 challenge (ie without this patch)
* verification will fail, but will pass with V = 0x1b or 0x1c.
*
* @param signature
* @return
*/
private byte[] patchSignatureVComponent(byte[] signature)
{
if (signature != null && signature.length == 65 && signature[64] < 27)

@ -38,6 +38,8 @@ import retrofit2.Response;
import retrofit2.http.Body;
import retrofit2.http.POST;
import static io.stormbird.wallet.entity.CryptoFunctions.sigFromByteArray;
/**
* Created by James on 7/02/2018.
*/
@ -378,18 +380,4 @@ public class MarketQueueService {
return recoveredKey;
}
public static Sign.SignatureData sigFromByteArray(byte[] sig)
{
byte subv = sig[64];
if (subv < 27) subv += 27;
byte[] subrRev = Arrays.copyOfRange(sig, 0, 32);
byte[] subsRev = Arrays.copyOfRange(sig, 32, 64);
BigInteger r = new BigInteger(1, subrRev);
BigInteger s = new BigInteger(1, subsRev);
return new Sign.SignatureData(subv, subrRev, subsRev);
}
}

@ -50,6 +50,10 @@ public class RealmManager {
return getRealmInstance("WalletData-db.realm");
}
public Realm getWalletTypeRealmInstance() {
return getRealmInstance("WalletType-db.realm");
}
private String get721Name(Wallet wallet) {
return wallet.address + "-721-db.realm";
}

@ -48,7 +48,6 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
BackupKeyViewModel viewModel;
private BackupState state;
private String keyBackup;
private Wallet wallet;
private TextView title;
private TextView detail;
@ -82,7 +81,6 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
BackupOperationType type = (BackupOperationType) getIntent().getSerializableExtra("TYPE");
keyBackup = getIntent().getStringExtra("ADDRESS");
wallet = getIntent().getParcelableExtra(WALLET);
layoutHolderWidth = 0;
if (type == null) type = BackupOperationType.UNDEFINED;
@ -143,7 +141,7 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
case KEYSTORE:
case KEYSTORE_LEGACY:
case HDKEY:
switch (viewModel.upgradeKeySecurity(wallet.address, this, this))
switch (viewModel.upgradeKeySecurity(wallet, this, this))
{
case REQUESTING_SECURITY:
//Do nothing, callback will return to 'CreatedKey()'. If it fails the returned key is empty
@ -169,19 +167,20 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
{
//key upgraded
//store wallet upgrade
Wallet wallet = new Wallet(address);
wallet.checkWalletType(this);
switch (wallet.type)
if (wallet.address.equalsIgnoreCase(address))
{
case KEYSTORE_LEGACY:
case KEYSTORE:
case HDKEY:
viewModel.upgradeWallet(address);
finishBackupSuccess(true);
break;
default:
cancelAuthentication();
break;
switch (wallet.type)
{
case KEYSTORE_LEGACY:
case KEYSTORE:
case HDKEY:
viewModel.upgradeWallet(address);
finishBackupSuccess(true);
break;
default:
cancelAuthentication();
break;
}
}
}
@ -376,7 +375,7 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
JSONBackup();
break;
case SET_JSON_PASSWORD:
viewModel.getPasswordForKeystore(keyBackup, this, this);
viewModel.getPasswordForKeystore(wallet, this, this);
break;
case SHOW_SEED_PHRASE:
VerifySeedPhrase();
@ -451,12 +450,9 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
private void backupKeySuccess(BackupOperationType type)
{
//first record backup time success, in case user aborts operation during key locking
viewModel.backupSuccess(keyBackup);
viewModel.backupSuccess(wallet);
//now ask if user wants to upgrade the key security (if required)
wallet = new Wallet(keyBackup);
wallet.checkWalletType(this);
switch (wallet.authLevel)
{
case STRONGBOX_NO_AUTHENTICATION:
@ -472,8 +468,6 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
private void finishBackupSuccess(boolean upgradeKey)
{
Wallet wallet = new Wallet(keyBackup);
wallet.checkWalletType(this);
Intent intent = new Intent();
switch (wallet.type)
{
@ -489,7 +483,7 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
break;
}
intent.putExtra("Key", keyBackup);
intent.putExtra("Key", wallet.address);
intent.putExtra("Upgrade", upgradeKey);
setResult(RESULT_OK, intent);
finish();
@ -578,7 +572,7 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
layoutWordHolder.removeAllViews();
}
viewModel.getSeedPhrase(keyBackup, this, this);
viewModel.getSeedPhrase(wallet, this, this);
}
private void addNewLayoutLine()
@ -651,7 +645,7 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
{
Intent intent = new Intent();
setResult(RESULT_CANCELED, intent);
intent.putExtra("Key", keyBackup);
intent.putExtra("Key", wallet.address);
finish();
}
}
@ -681,7 +675,7 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
{
Intent intent = new Intent();
setResult(RESULT_CANCELED, intent);
intent.putExtra("Key", keyBackup);
intent.putExtra("Key", wallet.address);
finish();
}
@ -697,7 +691,7 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
break;
case ENTER_JSON_BACKUP:
case SET_JSON_PASSWORD:
viewModel.exportWallet(keyBackup, mnemonic, inputView.getText().toString());
viewModel.exportWallet(wallet, mnemonic, inputView.getText().toString());
break;
case SHOW_SEED_PHRASE:
setupTestSeed();
@ -887,4 +881,31 @@ public class BackupKeyActivity extends BaseActivity implements View.OnClickListe
{
UNDEFINED, BACKUP_HD_KEY, BACKUP_KEYSTORE_KEY, SHOW_SEED_PHRASE, EXPORT_PRIVATE_KEY
}
// switch (operation)
// {
// case CREATE_HD_KEY:
// case CREATE_NON_AUTHENTICATED_KEY:
// if (callbackInterface != null)
// callbackInterface.HDKeyCreated(address, context, authLevel);
// break;
// case IMPORT_HD_KEY:
// importCallback.WalletValidated(address, authLevel);
// deleteNonAuthKeyEncryptedKeyBytes(address); //in the case the user re-imported a key, destroy the backup key
// break;
// case UPGRADE_HD_KEY:
// case UPGRADE_KEYSTORE_KEY:
// signCallback.CreatedKey(address);
// deleteNonAuthKeyEncryptedKeyBytes(address);
// break;
// case CREATE_KEYSTORE_KEY:
// importCallback.KeystoreValidated(new String(data), authLevel);
// break;
// case CREATE_PRIVATE_KEY:
// importCallback.KeyValidated(new String(data), authLevel);
// break;
// default:
// break;
// }
}

@ -650,7 +650,7 @@ public class DappBrowserFragment extends Fragment implements
{
messageBytes = Numeric.hexStringToByteArray(message.value);
}
viewModel.getAuthorisation(wallet.address, getActivity(), this);
viewModel.getAuthorisation(wallet, getActivity(), this);
}
else
{
@ -701,7 +701,7 @@ public class DappBrowserFragment extends Fragment implements
+ convertedMessage.length()
+ convertedMessage;
messageBytes = signMessage.getBytes();
viewModel.getAuthorisation(wallet.address, getActivity(), this);
viewModel.getAuthorisation(wallet, getActivity(), this);
});
dialog.setOnRejectListener(v -> {
web3.onSignCancel(message);

@ -98,6 +98,7 @@ public class FunctionActivity extends BaseActivity implements View.OnClickListen
tokenView.setVisibility(View.GONE);
waitSpinner.setVisibility(View.VISIBLE);
viewModel.startGasPriceUpdate(token.tokenInfo.chainId);
viewModel.getCurrentWallet();
getAttrs();
}
@ -425,7 +426,7 @@ public class FunctionActivity extends BaseActivity implements View.OnClickListen
//this is very specific but 'value' is a specifically handled param
value = action.function.tx.args.get("value").value;
BigDecimal valCorrected = getCorrectedBalance(value, 18);
Token currency = viewModel.getCurrency(token.tokenInfo.chainId, token.getWallet());
Token currency = viewModel.getCurrency(token.tokenInfo.chainId);
functionEffect = valCorrected.toString() + " " + currency.tokenInfo.symbol + " to " + actionMethod;
}
@ -453,7 +454,7 @@ public class FunctionActivity extends BaseActivity implements View.OnClickListen
//calculate native amount
BigDecimal value = new BigDecimal(function.tx.args.get("value").value);
//this is a native send, so check the native currency
Token currency = viewModel.getCurrency(token.tokenInfo.chainId, token.getWallet());
Token currency = viewModel.getCurrency(token.tokenInfo.chainId);
if (currency.balance.subtract(value).compareTo(BigDecimal.ZERO) < 0)
{
@ -529,9 +530,7 @@ public class FunctionActivity extends BaseActivity implements View.OnClickListen
@Override
public void signMessage(byte[] sign, DAppFunction dAppFunction, Message<String> message)
{
Wallet signingWallet = new Wallet(token.getWallet());
signingWallet.checkWalletType(this);
viewModel.signMessage(sign, dAppFunction, message, token.tokenInfo.chainId, signingWallet);
viewModel.signMessage(sign, dAppFunction, message, token.tokenInfo.chainId);
}
@Override
@ -554,7 +553,7 @@ public class FunctionActivity extends BaseActivity implements View.OnClickListen
dialog.setMessage(message.value);
dialog.setOnApproveListener(v -> {
messageToSign = message;
viewModel.getAuthorisation(token.getWallet(), this, this);
viewModel.getAuthorisation(this, this);
});
dialog.setOnRejectListener(v -> {
tokenView.onSignCancel(message);

@ -521,12 +521,11 @@ public class HomeActivity extends BaseNavigationActivity implements View.OnClick
{
//postpone backup until later
settingsFragment.backupSeedSuccess();
walletFragment.remindMeLater(keyBackup);
viewModel.checkIsBackedUp(keyBackup);
/*successImage.setImageResource(R.drawable.ic_error);
successOverlay.setVisibility(View.VISIBLE);
handler = new Handler();*/
if (keyBackup != null)
{
walletFragment.remindMeLater(new Wallet(keyBackup));
viewModel.checkIsBackedUp(keyBackup);
}
}
private void backupWalletSuccess(String keyBackup)

@ -340,27 +340,24 @@ public class ImportWalletActivity extends BaseActivity implements OnImportSeedLi
@Override
public void WalletValidated(String address, KeyService.AuthenticationLevel level)
{
importWalletViewModel.onSeed(address, level);
if (address == null) keyImportError(getString(R.string.import_error));
else importWalletViewModel.onSeed(address, level);
}
@Override
public void KeystoreValidated(String newPassword, KeyService.AuthenticationLevel level)
{
ImportKeystoreFragment importKeystoreFragment = (ImportKeystoreFragment) pages.get(ImportType.KEYSTORE_FORM_INDEX.ordinal()).second;
if (importKeystoreFragment != null)
{
importWalletViewModel.onKeystore(importKeystoreFragment.getKeystore(), importKeystoreFragment.getPassword(), newPassword, level);
}
if (importKeystoreFragment == null || newPassword == null) keyImportError(getString(R.string.import_error));
else importWalletViewModel.onKeystore(importKeystoreFragment.getKeystore(), importKeystoreFragment.getPassword(), newPassword, level);
}
@Override
public void KeyValidated(String newPassword, KeyService.AuthenticationLevel level)
{
ImportPrivateKeyFragment importPrivateKeyFragment = (ImportPrivateKeyFragment) pages.get(ImportType.PRIVATE_KEY_FORM_INDEX.ordinal()).second;
if (importPrivateKeyFragment != null)
{
importWalletViewModel.onPrivateKey(importPrivateKeyFragment.getPrivateKey(), newPassword, level);
}
if (importPrivateKeyFragment == null || newPassword == null) keyImportError(getString(R.string.import_error));
else importWalletViewModel.onPrivateKey(importPrivateKeyFragment.getPrivateKey(), newPassword, level);
}
@Override

@ -205,7 +205,6 @@ public class NewSettingsFragment extends Fragment
private void openBackupActivity(Wallet wallet)
{
Intent intent = new Intent(getContext(), BackupKeyActivity.class);
intent.putExtra("ADDRESS", wallet.address);
intent.putExtra(WALLET, wallet);
switch (wallet.type)
@ -337,7 +336,8 @@ public class NewSettingsFragment extends Fragment
layoutBackup.setVisibility(View.GONE);
}
//remove the number prompt
if (getActivity() !=null) ((HomeActivity) getActivity()).removeSettingsBadgeKey(C.KEY_NEEDS_BACKUP);
if (getActivity() != null) ((HomeActivity) getActivity()).removeSettingsBadgeKey(C.KEY_NEEDS_BACKUP);
onDefaultWallet(viewModel.defaultWallet().getValue());
}
}

@ -76,7 +76,6 @@ public class RedeemSignatureDisplayActivity extends BaseActivity implements View
viewModel.selection().observe(this, this::onSelected);
viewModel.burnNotice().observe(this, this::onBurned);
viewModel.signRequest().observe(this, this::onSignRequest);
wallet.checkWalletType(this);
ticketBurnNotice();
TextView tv = findViewById(R.id.textAddIDs);

@ -25,6 +25,7 @@ import io.stormbird.wallet.viewmodel.WalletActionsViewModelFactory;
import io.stormbird.wallet.widget.AWalletAlertDialog;
import static io.stormbird.wallet.C.*;
import static io.stormbird.wallet.C.Key.WALLET;
public class WalletActionsActivity extends BaseActivity implements View.OnClickListener, Runnable {
@Inject
@ -238,7 +239,7 @@ public class WalletActionsActivity extends BaseActivity implements View.OnClickL
private void testSeedPhrase(Wallet wallet)
{
Intent intent = new Intent(this, BackupKeyActivity.class);
intent.putExtra("ADDRESS", wallet.address);
intent.putExtra(WALLET, wallet);
intent.putExtra("TYPE", BackupKeyActivity.BackupOperationType.SHOW_SEED_PHRASE);
intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivityForResult(intent, C.REQUEST_BACKUP_WALLET);
@ -277,7 +278,7 @@ public class WalletActionsActivity extends BaseActivity implements View.OnClickL
private void exportJSON(Wallet wallet)
{
Intent intent = new Intent(this, BackupKeyActivity.class);
intent.putExtra("ADDRESS", wallet.address);
intent.putExtra(WALLET, wallet);
intent.putExtra("TYPE", BackupKeyActivity.BackupOperationType.BACKUP_KEYSTORE_KEY);
intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivityForResult(intent, C.REQUEST_BACKUP_WALLET);

@ -41,6 +41,7 @@ import java.math.BigInteger;
import java.util.*;
import static io.stormbird.wallet.C.ErrorCode.EMPTY_COLLECTION;
import static io.stormbird.wallet.C.Key.WALLET;
/**
* Created by justindeguzman on 2/28/18.
@ -258,7 +259,7 @@ public class WalletFragment extends Fragment implements OnTokenClickListener, Vi
wData.buttonText = getString(R.string.back_up_wallet_action, viewModel.getWalletAddr().substring(0, 5));
wData.colour = ContextCompat.getColor(getContext(), R.color.slate_grey);
wData.buttonColour = ContextCompat.getColor(getContext(), R.color.backup_grey);
wData.address = viewModel.getWalletAddr();
wData.wallet = viewModel.getWallet();
adapter.addWarning(wData);
break;
case WALLET_HAS_HIGH_VALUE:
@ -268,7 +269,7 @@ public class WalletFragment extends Fragment implements OnTokenClickListener, Vi
wData.buttonText = getString(R.string.back_up_wallet_action, viewModel.getWalletAddr().substring(0, 5));
wData.colour = ContextCompat.getColor(getContext(), R.color.warning_red);
wData.buttonColour = ContextCompat.getColor(getContext(), R.color.warning_dark_red);
wData.address = viewModel.getWalletAddr();
wData.wallet = viewModel.getWallet();
adapter.addWarning(wData);
break;
}
@ -410,10 +411,10 @@ public class WalletFragment extends Fragment implements OnTokenClickListener, Vi
}
@Override
public void BackupClick(String address)
public void BackupClick(Wallet wallet)
{
Intent intent = new Intent(getContext(), BackupKeyActivity.class);
intent.putExtra("ADDRESS", address);
intent.putExtra(WALLET, wallet);
switch (viewModel.getWalletType())
{
@ -430,9 +431,9 @@ public class WalletFragment extends Fragment implements OnTokenClickListener, Vi
}
@Override
public void remindMeLater(String walletAddress)
public void remindMeLater(Wallet wallet)
{
viewModel.setKeyWarningDismissTime(walletAddress).isDisposed();
viewModel.setKeyWarningDismissTime(wallet.address).isDisposed();
adapter.removeBackupWarning();
}
@ -457,7 +458,7 @@ public class WalletFragment extends Fragment implements OnTokenClickListener, Vi
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
remindMeLater(viewModel.getWalletAddr());
remindMeLater(viewModel.getWallet());
}
// @Override

@ -4,13 +4,11 @@ import android.app.Dialog;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomSheetBehavior;
@ -24,10 +22,17 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import javax.inject.Inject;
import dagger.android.AndroidInjection;
import io.stormbird.wallet.C;
import io.stormbird.wallet.R;
import io.stormbird.wallet.entity.*;
import io.stormbird.wallet.entity.CreateWalletCallbackInterface;
import io.stormbird.wallet.entity.ErrorEnvelope;
import io.stormbird.wallet.entity.NetworkInfo;
import io.stormbird.wallet.entity.PinAuthenticationCallbackInterface;
import io.stormbird.wallet.entity.Wallet;
import io.stormbird.wallet.service.KeyService;
import io.stormbird.wallet.ui.widget.adapter.WalletsAdapter;
import io.stormbird.wallet.viewmodel.WalletsViewModel;
@ -37,11 +42,6 @@ import io.stormbird.wallet.widget.AddWalletView;
import io.stormbird.wallet.widget.SignTransactionDialog;
import io.stormbird.wallet.widget.SystemView;
import javax.inject.Inject;
import java.util.List;
import java.util.Map;
public class WalletsActivity extends BaseActivity implements
View.OnClickListener,
AddWalletView.OnNewWalletClickListener,
@ -95,7 +95,6 @@ public class WalletsActivity extends BaseActivity implements
private void updateWalletName(Wallet wallet)
{
//systemView.showProgress(false);
adapter.updateWalletName(wallet);
}
@ -321,7 +320,8 @@ public class WalletsActivity extends BaseActivity implements
@Override
public void HDKeyCreated(String address, Context ctx, KeyService.AuthenticationLevel level)
{
viewModel.StoreHDWallet(address, level);
if (address == null) onCreateWalletError(new ErrorEnvelope(""));
else viewModel.StoreHDWallet(address, level);
}
@Override

@ -1,6 +1,7 @@
package io.stormbird.wallet.ui.widget.entity;
import io.stormbird.wallet.entity.BackupTokenCallback;
import io.stormbird.wallet.entity.Wallet;
/**
* Created by James on 18/07/2019.
@ -11,7 +12,7 @@ public class WarningData
public String title;
public String detail;
public String buttonText;
public String address;
public Wallet wallet;
public int colour;
public int buttonColour;
public final BackupTokenCallback callback;

@ -39,7 +39,7 @@ public class WarningHolder extends BinderViewHolder<WarningData>
layoutBackground.setBackgroundColor(data.colour);
backupButton.setText(data.buttonText);
backupButton.setBackgroundColor(data.buttonColour);
backupButton.setOnClickListener(v -> { data.callback.BackupClick(data.address); });
backupButton.setOnClickListener(v -> { data.callback.BackupClick(data.wallet); });
menuButton.setOnClickListener(v -> {
showPopup(popupAnchor, data);
});
@ -51,7 +51,7 @@ public class WarningHolder extends BinderViewHolder<WarningData>
int height = LinearLayout.LayoutParams.WRAP_CONTENT;
final PopupWindow popupWindow = new PopupWindow(popupView, width, height, true);
popupView.setOnClickListener(v -> {
data.callback.remindMeLater(data.address);
data.callback.remindMeLater(data.wallet);
popupWindow.dismiss();
});
popupWindow.showAsDropDown(view, 0, 20);

@ -65,9 +65,9 @@ public class BackupKeyViewModel extends BaseViewModel {
deleted.postValue(true);
}
public void exportWallet(String walletAddr, String keystorePassword, String storePassword) {
public void exportWallet(Wallet wallet, String keystorePassword, String storePassword) {
disposable = exportWalletInteract
.export(new Wallet(walletAddr), keystorePassword, storePassword)
.export(wallet, keystorePassword, storePassword)
.subscribe(exportedStore::postValue, this::onExportWalletError);
}
@ -103,6 +103,16 @@ public class BackupKeyViewModel extends BaseViewModel {
private void onWalletUpgraded(Wallet wallet)
{
switch (wallet.authLevel)
{
default:
case TEE_NO_AUTHENTICATION:
wallet.authLevel = KeyService.AuthenticationLevel.TEE_AUTHENTICATION;
break;
case STRONGBOX_NO_AUTHENTICATION:
wallet.authLevel = KeyService.AuthenticationLevel.STRONGBOX_AUTHENTICATION;
break;
}
Log.d("HVM", "Wallet " + wallet.address + " Upgraded to: " + wallet.authLevel.toString());
}
@ -127,24 +137,24 @@ public class BackupKeyViewModel extends BaseViewModel {
}
public KeyService.UpgradeKeyResult upgradeKeySecurity(String keyBackup, Activity activity, SignAuthenticationCallback callback)
public KeyService.UpgradeKeyResult upgradeKeySecurity(Wallet wallet, Activity activity, SignAuthenticationCallback callback)
{
return keyService.upgradeKeySecurity(keyBackup, activity, callback);
return keyService.upgradeKeySecurity(wallet, activity, callback);
}
public void getPasswordForKeystore(String keyBackup, Activity activity, CreateWalletCallbackInterface callback)
public void getPasswordForKeystore(Wallet wallet, Activity activity, CreateWalletCallbackInterface callback)
{
keyService.getPassword(keyBackup, activity, callback);
keyService.getPassword(wallet, activity, callback);
}
public void getSeedPhrase(String keyBackup, Activity activity, CreateWalletCallbackInterface callback)
public void getSeedPhrase(Wallet wallet, Activity activity, CreateWalletCallbackInterface callback)
{
keyService.getMnemonic(keyBackup, activity, callback);
keyService.getMnemonic(wallet, activity, callback);
}
public void backupSuccess(String keyBackup)
public void backupSuccess(Wallet wallet)
{
fetchWalletsInteract.updateBackupTime(keyBackup).isDisposed();
fetchWalletsInteract.updateBackupTime(wallet.address).isDisposed();
}
}

@ -242,7 +242,7 @@ public class ConfirmationViewModel extends BaseViewModel {
{
if (defaultWallet.getValue() != null)
{
keyService.getAuthenticationForSignature(defaultWallet.getValue().address, activity, callback);
keyService.getAuthenticationForSignature(defaultWallet.getValue(), activity, callback);
}
}
}

@ -306,8 +306,8 @@ public class DappBrowserViewModel extends BaseViewModel {
gasService.stopGasListener();
}
public void getAuthorisation(String walletAddress, Activity activity, SignAuthenticationCallback callback)
public void getAuthorisation(Wallet wallet, Activity activity, SignAuthenticationCallback callback)
{
keyService.getAuthenticationForSignature(walletAddress, activity, callback);
keyService.getAuthenticationForSignature(wallet, activity, callback);
}
}

@ -674,7 +674,7 @@ public class ImportTokenViewModel extends BaseViewModel
{
if (wallet.getValue() != null)
{
keyService.getAuthenticationForSignature(wallet.getValue().address, activity, callback);
keyService.getAuthenticationForSignature(wallet.getValue(), activity, callback);
}
}
}

@ -109,10 +109,10 @@ public class ImportWalletViewModel extends BaseViewModel implements OnSetWatchWa
}
}
public void getAuthorisation(String walletAddress, Activity activity, SignAuthenticationCallback callback)
{
keyService.getAuthenticationForSignature(walletAddress, activity, callback);
}
// public void getAuthorisation(String walletAddress, Activity activity, SignAuthenticationCallback callback)
// {
// keyService.getAuthenticationForSignature(walletAddress, activity, callback);
// }
public void importHDWallet(String seedPhrase, Activity activity, ImportWalletCallback callback)
{

@ -290,7 +290,7 @@ public class RedeemSignatureDisplayModel extends BaseViewModel
{
if (defaultWallet.getValue() != null)
{
keyService.getAuthenticationForSignature(defaultWallet.getValue().address, activity, callback);
keyService.getAuthenticationForSignature(defaultWallet.getValue(), activity, callback);
}
}
}

@ -142,7 +142,7 @@ public class SellDetailViewModel extends BaseViewModel {
{
if (defaultWallet.getValue() != null)
{
keyService.getAuthenticationForSignature(defaultWallet.getValue().address, activity, callback);
keyService.getAuthenticationForSignature(defaultWallet.getValue(), activity, callback);
}
}
}

@ -12,6 +12,8 @@ import io.stormbird.token.entity.TokenScriptResult;
import io.stormbird.wallet.C;
import io.stormbird.wallet.entity.*;
import io.stormbird.wallet.interact.CreateTransactionInteract;
import io.stormbird.wallet.interact.FetchWalletsInteract;
import io.stormbird.wallet.interact.GenericWalletInteract;
import io.stormbird.wallet.repository.EthereumNetworkRepositoryType;
import io.stormbird.wallet.router.SellTicketRouter;
import io.stormbird.wallet.router.TransferTicketDetailRouter;
@ -44,6 +46,8 @@ public class TokenFunctionViewModel extends BaseViewModel
private final TokensService tokensService;
private final EthereumNetworkRepositoryType ethereumNetworkRepository;
private final KeyService keyService;
private final GenericWalletInteract genericWalletInteract;
private Wallet wallet;
TokenFunctionViewModel(
AssetDefinitionService assetDefinitionService,
@ -53,7 +57,8 @@ public class TokenFunctionViewModel extends BaseViewModel
GasService gasService,
TokensService tokensService,
EthereumNetworkRepositoryType ethereumNetworkRepository,
KeyService keyService) {
KeyService keyService,
GenericWalletInteract genericWalletInteract) {
this.assetDefinitionService = assetDefinitionService;
this.sellTicketRouter = sellTicketRouter;
this.transferTicketRouter = transferTicketRouter;
@ -62,6 +67,7 @@ public class TokenFunctionViewModel extends BaseViewModel
this.tokensService = tokensService;
this.ethereumNetworkRepository = ethereumNetworkRepository;
this.keyService = keyService;
this.genericWalletInteract = genericWalletInteract;
}
public AssetDefinitionService getAssetDefinitionService()
@ -71,7 +77,7 @@ public class TokenFunctionViewModel extends BaseViewModel
public void openUniversalLink(Context context, Token token, String ticketIDs) {
Intent intent = new Intent(context, SellDetailActivity.class);
intent.putExtra(WALLET, new Wallet(token.getWallet()));
intent.putExtra(WALLET, wallet);
intent.putExtra(TICKET, token);
intent.putExtra(EXTRA_TOKENID_LIST, ticketIDs);
intent.putExtra(EXTRA_STATE, SellDetailActivity.SET_A_PRICE);
@ -90,13 +96,14 @@ public class TokenFunctionViewModel extends BaseViewModel
}
public void showTransferToken(Context context, Token token, String tokenIds)
{
transferTicketRouter.open(context, token, tokenIds, new Wallet(token.getWallet()));
transferTicketRouter.open(context, token, tokenIds, wallet);
}
public void showFunction(Context ctx, Token token, String method, List<BigInteger> tokenIds)
{
Intent intent = new Intent(ctx, FunctionActivity.class);
intent.putExtra(TICKET, token);
intent.putExtra(WALLET, wallet);
intent.putExtra(C.EXTRA_STATE, method);
BigInteger firstId = tokenIds != null ? tokenIds.get(0) : BigInteger.ZERO;
intent.putExtra(C.EXTRA_TOKEN_ID, firstId.toString(16));
@ -108,14 +115,14 @@ public class TokenFunctionViewModel extends BaseViewModel
TicketRangeParcel parcel = new TicketRangeParcel(new TicketRange(ids, token.getAddress(), true));
Intent intent = new Intent(ctx, RedeemSignatureDisplayActivity.class);
intent.putExtra(WALLET, new Wallet(token.getWallet()));
intent.putExtra(WALLET, wallet);
intent.putExtra(TICKET, token);
intent.putExtra(TICKET_RANGE, parcel);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
ctx.startActivity(intent);
}
public void signMessage(byte[] signRequest, DAppFunction dAppFunction, Message<String> message, int chainId, Wallet wallet) {
public void signMessage(byte[] signRequest, DAppFunction dAppFunction, Message<String> message, int chainId) {
disposable = createTransactionInteract.sign(wallet, signRequest, chainId)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
@ -185,13 +192,20 @@ public class TokenFunctionViewModel extends BaseViewModel
gasService.stopGasListener();
}
public void getAuthorisation(String walletAddress, Activity activity, SignAuthenticationCallback callback)
public void getAuthorisation(Activity activity, SignAuthenticationCallback callback)
{
keyService.getAuthenticationForSignature(walletAddress, activity, callback);
keyService.getAuthenticationForSignature(wallet, activity, callback);
}
public Token getCurrency(int chainId, String walletAddr)
public Token getCurrency(int chainId)
{
return tokensService.getToken(chainId, walletAddr);
return tokensService.getToken(chainId, wallet.address);
}
public void getCurrentWallet()
{
disposable = genericWalletInteract
.find()
.subscribe(wallet -> { this.wallet = wallet; }, this::onError);
}
}

@ -4,6 +4,7 @@ import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProvider;
import android.support.annotation.NonNull;
import io.stormbird.wallet.interact.CreateTransactionInteract;
import io.stormbird.wallet.interact.GenericWalletInteract;
import io.stormbird.wallet.repository.EthereumNetworkRepositoryType;
import io.stormbird.wallet.router.SellTicketRouter;
import io.stormbird.wallet.router.TransferTicketDetailRouter;
@ -26,6 +27,7 @@ public class TokenFunctionViewModelFactory implements ViewModelProvider.Factory
private final TokensService tokensService;
private final EthereumNetworkRepositoryType ethereumNetworkRepository;
private final KeyService keyService;
private final GenericWalletInteract genericWalletInteract;
public TokenFunctionViewModelFactory(
AssetDefinitionService assetDefinitionService,
@ -35,7 +37,8 @@ public class TokenFunctionViewModelFactory implements ViewModelProvider.Factory
GasService gasService,
TokensService tokensService,
EthereumNetworkRepositoryType ethereumNetworkRepository,
KeyService keyService) {
KeyService keyService,
GenericWalletInteract genericWalletInteract) {
this.assetDefinitionService = assetDefinitionService;
this.sellTicketRouter = sellTicketRouter;
this.transferTicketRouter = transferTicketRouter;
@ -44,11 +47,12 @@ public class TokenFunctionViewModelFactory implements ViewModelProvider.Factory
this.tokensService = tokensService;
this.ethereumNetworkRepository = ethereumNetworkRepository;
this.keyService = keyService;
this.genericWalletInteract = genericWalletInteract;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new TokenFunctionViewModel(assetDefinitionService, sellTicketRouter, transferTicketRouter, createTransactionInteract, gasService, tokensService, ethereumNetworkRepository, keyService);
return (T) new TokenFunctionViewModel(assetDefinitionService, sellTicketRouter, transferTicketRouter, createTransactionInteract, gasService, tokensService, ethereumNetworkRepository, keyService, genericWalletInteract);
}
}

@ -274,7 +274,7 @@ public class TransferTicketDetailViewModel extends BaseViewModel {
{
if (defaultWallet.getValue() != null)
{
keyService.getAuthenticationForSignature(defaultWallet.getValue().address, activity, callback);
keyService.getAuthenticationForSignature(defaultWallet.getValue(), activity, callback);
}
}
}

@ -623,4 +623,9 @@ public class WalletViewModel extends BaseViewModel
{
return genericWalletInteract.updateWarningTime(walletAddr);
}
public Wallet getWallet()
{
return currentWallet;
}
}

@ -92,7 +92,7 @@ public class WalletsViewModel extends BaseViewModel
this.keyService = keyService;
this.gasService = gasService;
executorService = Executors.newFixedThreadPool(4);
executorService = Executors.newFixedThreadPool(10);
}
public LiveData<Wallet[]> wallets()
@ -207,7 +207,14 @@ public class WalletsViewModel extends BaseViewModel
private void updateOnName(Wallet wallet)
{
if (wallet.ENSname != null && wallet.ENSname.length() > 0) updateENSName.postValue(wallet);
if (wallet.ENSname != null && wallet.ENSname.length() > 0)
{
//updateENSName.postValue(wallet);
disposable = fetchWalletsInteract.updateWalletData(wallet)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(updateENSName::postValue, this::onError);
}
}
private Observable<Wallet> resolveEns(Wallet wallet)
@ -347,6 +354,7 @@ public class WalletsViewModel extends BaseViewModel
{
Wallet wallet = new Wallet(address);
wallet.type = WalletType.HDKEY;
wallet.authLevel = authLevel;
fetchWalletsInteract.storeWallet(wallet)
.subscribe(account -> {
fetchWallets();

@ -177,4 +177,5 @@
<string name="reimport_wallet_detail">Wallet %1$s already exists, do you wish to re-import the wallet?</string>
<string name="reimport_wallet_title">Re-Import Wallet?</string>
<string name="provide_authentication">Provide authentication to create key</string>
<string name="import_error">Import Error, Try re-import again.</string>
</resources>

@ -443,4 +443,5 @@
<string name="reimport_wallet_detail">Wallet %1$s already exists, do you wish to re-import the wallet?</string>
<string name="reimport_wallet_title">Re-Import Wallet?</string>
<string name="provide_authentication">Provide authentication to create key</string>
<string name="import_error">Import Error, Try re-import again.</string>
</resources>

@ -579,4 +579,5 @@
<string name="reimport_wallet_detail">Wallet %1$s already exists, do you wish to re-import the wallet?</string>
<string name="reimport_wallet_title">Re-Import Wallet?</string>
<string name="provide_authentication">Provide authentication to create key</string>
<string name="import_error">Import Error, Try re-import again.</string>
</resources>

@ -87,7 +87,7 @@ public class AssetGenerationTest
String city = String.format("%02x", 1);
//String spacer = "FFFFFFFFFFFFFFFFFFFF";
String spacer = "00000000000000000000000000";
StringBuilder sb = new StringBuilder();
sb.append("[");
boolean first = true;

@ -28,7 +28,8 @@ public class CryptoFunctions implements CryptoFunctionsInterface
public BigInteger signedMessageToKey(byte[] data, byte[] signature) throws SignatureException
{
Sign.SignatureData sigData = sigFromByteArray(signature);
return Sign.signedMessageToKey(data, sigData);
if (sigData == null) return BigInteger.ZERO;
else return Sign.signedMessageToKey(data, sigData);
}
@Override
@ -39,6 +40,8 @@ public class CryptoFunctions implements CryptoFunctionsInterface
public static Sign.SignatureData sigFromByteArray(byte[] sig)
{
if (sig.length < 64 || sig.length > 65) return null;
byte subv = sig[64];
if (subv < 27) subv += 27;

Loading…
Cancel
Save