mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06: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();
|
WalletBackupAndKey walletBackupAndKey = storage.loadUnencryptedWallet();
|
||||||
openWallet(storage, walletBackupAndKey, this, forceSameWindow);
|
openWallet(storage, walletBackupAndKey, this, forceSameWindow);
|
||||||
} else {
|
} 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();
|
Optional<SecureString> optionalPassword = dlg.showAndWait();
|
||||||
if(optionalPassword.isEmpty()) {
|
if(optionalPassword.isEmpty()) {
|
||||||
return;
|
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);
|
dirtyPersistablesMap.remove(wallet);
|
||||||
} finally {
|
} finally {
|
||||||
walletDao.setSchema(DEFAULT_SCHEMA);
|
walletDao.setSchema(DEFAULT_SCHEMA);
|
||||||
|
@ -346,7 +353,7 @@ public class DbPersistence implements Persistence {
|
||||||
String newPassword = getFilePassword(encryptionPubKey);
|
String newPassword = getFilePassword(encryptionPubKey);
|
||||||
String currentPassword = getDatasourcePassword();
|
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 != null && !Objects.equals(currentPassword, newPassword)) {
|
||||||
if(!dataSource.isClosed()) {
|
if(!dataSource.isClosed()) {
|
||||||
dataSource.close();
|
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 {
|
private static class DirtyPersistables {
|
||||||
public boolean clearHistory;
|
public boolean clearHistory;
|
||||||
public final List<WalletNode> historyNodes = new ArrayList<>();
|
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<Entry> labelEntries = new ArrayList<>();
|
||||||
public final List<BlockTransactionHashIndex> utxoStatuses = new ArrayList<>();
|
public final List<BlockTransactionHashIndex> utxoStatuses = new ArrayList<>();
|
||||||
public final List<Keystore> labelKeystores = new ArrayList<>();
|
public final List<Keystore> labelKeystores = new ArrayList<>();
|
||||||
|
public final List<Keystore> encryptionKeystores = new ArrayList<>();
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Dirty Persistables" +
|
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()) +
|
"\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 labels:" + labelEntries.stream().filter(entry -> entry instanceof HashIndexEntry).map(entry -> ((HashIndexEntry)entry).getHashIndex().toString()).collect(Collectors.toList()) +
|
||||||
"\nUTXO statuses:" + utxoStatuses +
|
"\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")
|
@GetGeneratedKeys("id")
|
||||||
long insertMasterPrivateExtendedKey(byte[] privateKey, byte[] chainCode, byte[] initialisationVector, byte[] encryptedBytes, byte[] keySalt, Integer deriver, Integer crypter, long creationTimeSeconds);
|
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 (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
@SqlUpdate("insert into seed (type, mnemonicString, initialisationVector, encryptedBytes, keySalt, deriver, crypter, needsPassphrase, creationTimeSeconds) values (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||||
@GetGeneratedKeys("id")
|
@GetGeneratedKeys("id")
|
||||||
long insertSeed(int type, String mnemonicString, byte[] initialisationVector, byte[] encryptedBytes, byte[] keySalt, Integer deriver, Integer crypter, boolean needsPassphrase, long creationTimeSeconds);
|
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 = ?")
|
@SqlUpdate("update keystore set label = ? where id = ?")
|
||||||
void updateLabel(String label, long id);
|
void updateLabel(String label, long id);
|
||||||
|
|
||||||
default void addKeystores(Wallet wallet) {
|
default void addKeystores(Wallet wallet) {
|
||||||
for(int i = 0; i < wallet.getKeystores().size(); i++) {
|
for(int i = 0; i < wallet.getKeystores().size(); i++) {
|
||||||
Keystore keystore = wallet.getKeystores().get(i);
|
Keystore keystore = wallet.getKeystores().get(i);
|
||||||
if(keystore.getMasterPrivateExtendedKey() != null) {
|
if(keystore.hasMasterPrivateExtendedKey()) {
|
||||||
MasterPrivateExtendedKey mpek = keystore.getMasterPrivateExtendedKey();
|
MasterPrivateExtendedKey mpek = keystore.getMasterPrivateExtendedKey();
|
||||||
if(mpek.isEncrypted()) {
|
if(mpek.isEncrypted()) {
|
||||||
EncryptedData data = mpek.getEncryptedData();
|
EncryptedData data = mpek.getEncryptedData();
|
||||||
|
@ -50,7 +56,7 @@ public interface KeystoreDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(keystore.getSeed() != null) {
|
if(keystore.hasSeed()) {
|
||||||
DeterministicSeed seed = keystore.getSeed();
|
DeterministicSeed seed = keystore.getSeed();
|
||||||
if(seed.isEncrypted()) {
|
if(seed.isEncrypted()) {
|
||||||
EncryptedData data = seed.getEncryptedData();
|
EncryptedData data = seed.getEncryptedData();
|
||||||
|
@ -71,4 +77,26 @@ public interface KeystoreDao {
|
||||||
keystore.setId(id);
|
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;
|
package com.sparrowwallet.sparrow.wallet;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.policy.Policy;
|
import com.sparrowwallet.drongo.policy.Policy;
|
||||||
|
import com.sparrowwallet.drongo.wallet.DeterministicSeed;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
import com.sparrowwallet.drongo.wallet.MasterPrivateExtendedKey;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.sparrow.AppServices;
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
|
import com.sparrowwallet.sparrow.event.KeystoreEncryptionChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.event.KeystoreLabelsChangedEvent;
|
import com.sparrowwallet.sparrow.event.KeystoreLabelsChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.event.WalletAddressesChangedEvent;
|
import com.sparrowwallet.sparrow.event.WalletAddressesChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.event.WalletPasswordChangedEvent;
|
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()));
|
EventManager.get().post(new WalletAddressesChangedEvent(wallet, addressChange ? null : pastWallet, getWalletId()));
|
||||||
} else {
|
} else {
|
||||||
List<Keystore> changedKeystores = new ArrayList<>();
|
List<Keystore> labelChangedKeystores = getLabelChangedKeystores(wallet, walletCopy);
|
||||||
for(int i = 0; i < wallet.getKeystores().size(); i++) {
|
if(!labelChangedKeystores.isEmpty()) {
|
||||||
Keystore keystore = wallet.getKeystores().get(i);
|
EventManager.get().post(new KeystoreLabelsChangedEvent(wallet, pastWallet, getWalletId(), labelChangedKeystores));
|
||||||
Keystore keystoreCopy = walletCopy.getKeystores().get(i);
|
|
||||||
if(!Objects.equals(keystore.getLabel(), keystoreCopy.getLabel())) {
|
|
||||||
keystore.setLabel(keystoreCopy.getLabel());
|
|
||||||
changedKeystores.add(keystore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!changedKeystores.isEmpty()) {
|
List<Keystore> encryptionChangedKeystores = getEncryptionChangedKeystores(wallet, walletCopy);
|
||||||
EventManager.get().post(new KeystoreLabelsChangedEvent(wallet, pastWallet, getWalletId(), changedKeystores));
|
if(!encryptionChangedKeystores.isEmpty()) {
|
||||||
} else {
|
EventManager.get().post(new KeystoreEncryptionChangedEvent(wallet, pastWallet, getWalletId(), encryptionChangedKeystores));
|
||||||
//Can only be a password update at this point
|
}
|
||||||
|
|
||||||
|
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()));
|
EventManager.get().post(new WalletPasswordChangedEvent(wallet, pastWallet, getWalletId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,4 +187,47 @@ public class SettingsWalletForm extends WalletForm {
|
||||||
private Integer getNumSignaturesRequired(Policy policy) {
|
private Integer getNumSignaturesRequired(Policy policy) {
|
||||||
return policy == null ? null : policy.getNumSignaturesRequired();
|
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
|
@Subscribe
|
||||||
public void walletPasswordChanged(WalletPasswordChangedEvent event) {
|
public void walletPasswordChanged(WalletPasswordChangedEvent event) {
|
||||||
if(event.getWalletId().equals(getWalletId())) {
|
if(event.getWalletId().equals(getWalletId())) {
|
||||||
|
|
Loading…
Reference in a new issue