mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
update encrypted seeds and private keys when wallet password changes
This commit is contained in:
parent
cfac2768ae
commit
f1510de360
7 changed files with 132 additions and 18 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit 485e8c825b3beb9dc4bda3f3c2667264915dfd28
|
||||
Subproject commit f407547c4766fd81cea29768e275c250b06cf968
|
|
@ -766,7 +766,7 @@ public class AppController implements Initializable {
|
|||
WalletBackupAndKey walletBackupAndKey = storage.loadUnencryptedWallet();
|
||||
openWallet(storage, walletBackupAndKey, this, forceSameWindow);
|
||||
} else {
|
||||
WalletPasswordDialog dlg = new WalletPasswordDialog(file.getName(), WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
WalletPasswordDialog dlg = new WalletPasswordDialog(storage.getWalletName(null), WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
Optional<SecureString> optionalPassword = dlg.showAndWait();
|
||||
if(optionalPassword.isEmpty()) {
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KeystoreEncryptionChangedEvent extends WalletSettingsChangedEvent {
|
||||
private List<Keystore> changedKeystores;
|
||||
|
||||
public KeystoreEncryptionChangedEvent(Wallet wallet, Wallet pastWallet, String walletId, List<Keystore> changedKeystores) {
|
||||
super(wallet, pastWallet, walletId);
|
||||
this.changedKeystores = changedKeystores;
|
||||
}
|
||||
|
||||
public List<Keystore> getChangedKeystores() {
|
||||
return changedKeystores;
|
||||
}
|
||||
}
|
|
@ -269,6 +269,13 @@ public class DbPersistence implements Persistence {
|
|||
}
|
||||
}
|
||||
|
||||
if(!dirtyPersistables.encryptionKeystores.isEmpty()) {
|
||||
KeystoreDao keystoreDao = handle.attach(KeystoreDao.class);
|
||||
for(Keystore keystore : dirtyPersistables.encryptionKeystores) {
|
||||
keystoreDao.updateKeystoreEncryption(keystore);
|
||||
}
|
||||
}
|
||||
|
||||
dirtyPersistablesMap.remove(wallet);
|
||||
} finally {
|
||||
walletDao.setSchema(DEFAULT_SCHEMA);
|
||||
|
@ -346,7 +353,7 @@ public class DbPersistence implements Persistence {
|
|||
String newPassword = getFilePassword(encryptionPubKey);
|
||||
String currentPassword = getDatasourcePassword();
|
||||
|
||||
//The password only needs to be changed if the datasource is null - either we have loaded the wallet from a datasource, or it is a new wallet and the datasource is still to be created
|
||||
//The password only needs to be changed if the datasource is not null - if we have not loaded the wallet from a datasource, it is a new wallet and the database is still to be created
|
||||
if(dataSource != null && !Objects.equals(currentPassword, newPassword)) {
|
||||
if(!dataSource.isClosed()) {
|
||||
dataSource.close();
|
||||
|
@ -587,6 +594,13 @@ public class DbPersistence implements Persistence {
|
|||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void keystoreEncryptionChanged(KeystoreEncryptionChangedEvent event) {
|
||||
if(persistsFor(event.getWallet())) {
|
||||
dirtyPersistablesMap.computeIfAbsent(event.getWallet(), key -> new DirtyPersistables()).encryptionKeystores.addAll(event.getChangedKeystores());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DirtyPersistables {
|
||||
public boolean clearHistory;
|
||||
public final List<WalletNode> historyNodes = new ArrayList<>();
|
||||
|
@ -594,6 +608,7 @@ public class DbPersistence implements Persistence {
|
|||
public final List<Entry> labelEntries = new ArrayList<>();
|
||||
public final List<BlockTransactionHashIndex> utxoStatuses = new ArrayList<>();
|
||||
public final List<Keystore> labelKeystores = new ArrayList<>();
|
||||
public final List<Keystore> encryptionKeystores = new ArrayList<>();
|
||||
|
||||
public String toString() {
|
||||
return "Dirty Persistables" +
|
||||
|
@ -604,7 +619,8 @@ public class DbPersistence implements Persistence {
|
|||
"\nAddress labels:" + labelEntries.stream().filter(entry -> entry instanceof NodeEntry).map(entry -> ((NodeEntry)entry).getNode().toString() + " " + entry.getLabel()).collect(Collectors.toList()) +
|
||||
"\nUTXO labels:" + labelEntries.stream().filter(entry -> entry instanceof HashIndexEntry).map(entry -> ((HashIndexEntry)entry).getHashIndex().toString()).collect(Collectors.toList()) +
|
||||
"\nUTXO statuses:" + utxoStatuses +
|
||||
"\nKeystore labels:" + labelKeystores.stream().map(Keystore::getLabel).collect(Collectors.toList());
|
||||
"\nKeystore labels:" + labelKeystores.stream().map(Keystore::getLabel).collect(Collectors.toList()) +
|
||||
"\nKeystore encryptions:" + encryptionKeystores.stream().map(Keystore::getLabel).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,17 +28,23 @@ public interface KeystoreDao {
|
|||
@GetGeneratedKeys("id")
|
||||
long insertMasterPrivateExtendedKey(byte[] privateKey, byte[] chainCode, byte[] initialisationVector, byte[] encryptedBytes, byte[] keySalt, Integer deriver, Integer crypter, long creationTimeSeconds);
|
||||
|
||||
@SqlUpdate("update masterPrivateExtendedKey set privateKey = ?, chainCode = ?, initialisationVector = ?, encryptedBytes = ?, keySalt = ?, deriver = ?, crypter = ?, creationTimeSeconds = ? where id = ?")
|
||||
void updateMasterPrivateExtendedKey(byte[] privateKey, byte[] chainCode, byte[] initialisationVector, byte[] encryptedBytes, byte[] keySalt, Integer deriver, Integer crypter, long creationTimeSeconds, long id);
|
||||
|
||||
@SqlUpdate("insert into seed (type, mnemonicString, initialisationVector, encryptedBytes, keySalt, deriver, crypter, needsPassphrase, creationTimeSeconds) values (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||
@GetGeneratedKeys("id")
|
||||
long insertSeed(int type, String mnemonicString, byte[] initialisationVector, byte[] encryptedBytes, byte[] keySalt, Integer deriver, Integer crypter, boolean needsPassphrase, long creationTimeSeconds);
|
||||
|
||||
@SqlUpdate("update seed set type = ?, mnemonicString = ?, initialisationVector = ?, encryptedBytes = ?, keySalt = ?, deriver = ?, crypter = ?, needsPassphrase = ?, creationTimeSeconds = ? where id = ?")
|
||||
void updateSeed(int type, String mnemonicString, byte[] initialisationVector, byte[] encryptedBytes, byte[] keySalt, Integer deriver, Integer crypter, boolean needsPassphrase, long creationTimeSeconds, long id);
|
||||
|
||||
@SqlUpdate("update keystore set label = ? where id = ?")
|
||||
void updateLabel(String label, long id);
|
||||
|
||||
default void addKeystores(Wallet wallet) {
|
||||
for(int i = 0; i < wallet.getKeystores().size(); i++) {
|
||||
Keystore keystore = wallet.getKeystores().get(i);
|
||||
if(keystore.getMasterPrivateExtendedKey() != null) {
|
||||
if(keystore.hasMasterPrivateExtendedKey()) {
|
||||
MasterPrivateExtendedKey mpek = keystore.getMasterPrivateExtendedKey();
|
||||
if(mpek.isEncrypted()) {
|
||||
EncryptedData data = mpek.getEncryptedData();
|
||||
|
@ -50,7 +56,7 @@ public interface KeystoreDao {
|
|||
}
|
||||
}
|
||||
|
||||
if(keystore.getSeed() != null) {
|
||||
if(keystore.hasSeed()) {
|
||||
DeterministicSeed seed = keystore.getSeed();
|
||||
if(seed.isEncrypted()) {
|
||||
EncryptedData data = seed.getEncryptedData();
|
||||
|
@ -71,4 +77,26 @@ public interface KeystoreDao {
|
|||
keystore.setId(id);
|
||||
}
|
||||
}
|
||||
|
||||
default void updateKeystoreEncryption(Keystore keystore) {
|
||||
if(keystore.hasMasterPrivateExtendedKey()) {
|
||||
MasterPrivateExtendedKey mpek = keystore.getMasterPrivateExtendedKey();
|
||||
if(mpek.isEncrypted()) {
|
||||
EncryptedData data = mpek.getEncryptedData();
|
||||
updateMasterPrivateExtendedKey(null, null, data.getInitialisationVector(), data.getEncryptedBytes(), data.getKeySalt(), data.getEncryptionType().getDeriver().ordinal(), data.getEncryptionType().getCrypter().ordinal(), mpek.getCreationTimeSeconds(), mpek.getId());
|
||||
} else {
|
||||
updateMasterPrivateExtendedKey(mpek.getPrivateKey().getPrivKeyBytes(), mpek.getPrivateKey().getChainCode(), null, null, null, null, null, mpek.getCreationTimeSeconds(), mpek.getId());
|
||||
}
|
||||
}
|
||||
|
||||
if(keystore.hasSeed()) {
|
||||
DeterministicSeed seed = keystore.getSeed();
|
||||
if(seed.isEncrypted()) {
|
||||
EncryptedData data = seed.getEncryptedData();
|
||||
updateSeed(seed.getType().ordinal(), null, data.getInitialisationVector(), data.getEncryptedBytes(), data.getKeySalt(), data.getEncryptionType().getDeriver().ordinal(), data.getEncryptionType().getCrypter().ordinal(), seed.needsPassphrase(), seed.getCreationTimeSeconds(), seed.getId());
|
||||
} else {
|
||||
updateSeed(seed.getType().ordinal(), seed.getMnemonicString().asString(), null, null, null, null, null, seed.needsPassphrase(), seed.getCreationTimeSeconds(), seed.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package com.sparrowwallet.sparrow.wallet;
|
||||
|
||||
import com.sparrowwallet.drongo.policy.Policy;
|
||||
import com.sparrowwallet.drongo.wallet.DeterministicSeed;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.MasterPrivateExtendedKey;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.KeystoreEncryptionChangedEvent;
|
||||
import com.sparrowwallet.sparrow.event.KeystoreLabelsChangedEvent;
|
||||
import com.sparrowwallet.sparrow.event.WalletAddressesChangedEvent;
|
||||
import com.sparrowwallet.sparrow.event.WalletPasswordChangedEvent;
|
||||
|
@ -82,20 +85,18 @@ public class SettingsWalletForm extends WalletForm {
|
|||
|
||||
EventManager.get().post(new WalletAddressesChangedEvent(wallet, addressChange ? null : pastWallet, getWalletId()));
|
||||
} else {
|
||||
List<Keystore> changedKeystores = new ArrayList<>();
|
||||
for(int i = 0; i < wallet.getKeystores().size(); i++) {
|
||||
Keystore keystore = wallet.getKeystores().get(i);
|
||||
Keystore keystoreCopy = walletCopy.getKeystores().get(i);
|
||||
if(!Objects.equals(keystore.getLabel(), keystoreCopy.getLabel())) {
|
||||
keystore.setLabel(keystoreCopy.getLabel());
|
||||
changedKeystores.add(keystore);
|
||||
}
|
||||
List<Keystore> labelChangedKeystores = getLabelChangedKeystores(wallet, walletCopy);
|
||||
if(!labelChangedKeystores.isEmpty()) {
|
||||
EventManager.get().post(new KeystoreLabelsChangedEvent(wallet, pastWallet, getWalletId(), labelChangedKeystores));
|
||||
}
|
||||
|
||||
if(!changedKeystores.isEmpty()) {
|
||||
EventManager.get().post(new KeystoreLabelsChangedEvent(wallet, pastWallet, getWalletId(), changedKeystores));
|
||||
} else {
|
||||
//Can only be a password update at this point
|
||||
List<Keystore> encryptionChangedKeystores = getEncryptionChangedKeystores(wallet, walletCopy);
|
||||
if(!encryptionChangedKeystores.isEmpty()) {
|
||||
EventManager.get().post(new KeystoreEncryptionChangedEvent(wallet, pastWallet, getWalletId(), encryptionChangedKeystores));
|
||||
}
|
||||
|
||||
if(labelChangedKeystores.isEmpty() && encryptionChangedKeystores.isEmpty()) {
|
||||
//Can only be a wallet password change on a wallet without private keys
|
||||
EventManager.get().post(new WalletPasswordChangedEvent(wallet, pastWallet, getWalletId()));
|
||||
}
|
||||
}
|
||||
|
@ -186,4 +187,47 @@ public class SettingsWalletForm extends WalletForm {
|
|||
private Integer getNumSignaturesRequired(Policy policy) {
|
||||
return policy == null ? null : policy.getNumSignaturesRequired();
|
||||
}
|
||||
|
||||
private List<Keystore> getLabelChangedKeystores(Wallet original, Wallet changed) {
|
||||
List<Keystore> changedKeystores = new ArrayList<>();
|
||||
for(int i = 0; i < original.getKeystores().size(); i++) {
|
||||
Keystore originalKeystore = original.getKeystores().get(i);
|
||||
Keystore changedKeystore = changed.getKeystores().get(i);
|
||||
|
||||
if(!Objects.equals(originalKeystore.getLabel(), changedKeystore.getLabel())) {
|
||||
originalKeystore.setLabel(changedKeystore.getLabel());
|
||||
changedKeystores.add(originalKeystore);
|
||||
}
|
||||
}
|
||||
|
||||
return changedKeystores;
|
||||
}
|
||||
|
||||
private List<Keystore> getEncryptionChangedKeystores(Wallet original, Wallet changed) {
|
||||
List<Keystore> changedKeystores = new ArrayList<>();
|
||||
for(int i = 0; i < original.getKeystores().size(); i++) {
|
||||
Keystore originalKeystore = original.getKeystores().get(i);
|
||||
Keystore changedKeystore = changed.getKeystores().get(i);
|
||||
|
||||
if(originalKeystore.hasSeed() && changedKeystore.hasSeed()) {
|
||||
if(!Objects.equals(originalKeystore.getSeed().getEncryptedData(), changedKeystore.getSeed().getEncryptedData())) {
|
||||
DeterministicSeed changedSeed = changedKeystore.getSeed().copy();
|
||||
changedSeed.setId(originalKeystore.getSeed().getId());
|
||||
originalKeystore.setSeed(changedSeed);
|
||||
changedKeystores.add(originalKeystore);
|
||||
}
|
||||
}
|
||||
|
||||
if(originalKeystore.hasMasterPrivateExtendedKey() && changedKeystore.hasMasterPrivateExtendedKey()) {
|
||||
if(!Objects.equals(originalKeystore.getMasterPrivateExtendedKey().getEncryptedData(), changedKeystore.getMasterPrivateExtendedKey().getEncryptedData())) {
|
||||
MasterPrivateExtendedKey changedMpek = changedKeystore.getMasterPrivateExtendedKey().copy();
|
||||
changedMpek.setId(originalKeystore.getMasterPrivateExtendedKey().getId());
|
||||
originalKeystore.setMasterPrivateExtendedKey(changedMpek);
|
||||
changedKeystores.add(originalKeystore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changedKeystores;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -330,6 +330,13 @@ public class WalletForm {
|
|||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void keystoreEncryptionChanged(KeystoreEncryptionChangedEvent event) {
|
||||
if(event.getWalletId().equals(getWalletId())) {
|
||||
Platform.runLater(() -> EventManager.get().post(new WalletDataChangedEvent(wallet)));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletPasswordChanged(WalletPasswordChangedEvent event) {
|
||||
if(event.getWalletId().equals(getWalletId())) {
|
||||
|
|
Loading…
Reference in a new issue