passphrase not stored

This commit is contained in:
Craig Raw
2020-05-16 15:09:47 +02:00
parent 1bf8c85a65
commit 6b09dc0293
15 changed files with 93 additions and 111 deletions
@@ -4,14 +4,13 @@ import com.google.common.base.Charsets;
import com.google.common.eventbus.Subscribe;
import com.google.common.io.ByteSource;
import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.crypto.ECIESKeyCrypter;
import com.sparrowwallet.drongo.crypto.ECKey;
import com.sparrowwallet.drongo.crypto.InvalidPasswordException;
import com.sparrowwallet.drongo.crypto.*;
import com.sparrowwallet.drongo.policy.PolicyType;
import com.sparrowwallet.drongo.protocol.ScriptType;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.psbt.PSBTParseException;
import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.control.*;
import com.sparrowwallet.sparrow.event.*;
@@ -235,24 +234,65 @@ public class AppController implements Initializable {
if(file != null) {
try {
Wallet wallet;
String password = null;
ECKey encryptionPubKey = WalletForm.NO_PASSWORD_KEY;
FileType fileType = IOUtils.getFileType(file);
if(FileType.JSON.equals(fileType)) {
wallet = Storage.getStorage().loadWallet(file);
} else if(FileType.BINARY.equals(fileType)) {
WalletPasswordDialog dlg = new WalletPasswordDialog(WalletPasswordDialog.PasswordRequirement.LOAD);
Optional<String> password = dlg.showAndWait();
if(!password.isPresent()) {
Optional<String> optionalPassword = dlg.showAndWait();
if(!optionalPassword.isPresent()) {
return;
}
ECKey encryptionFullKey = ECIESKeyCrypter.deriveECKey(password.get());
password = optionalPassword.get();
ECKey encryptionFullKey = ECIESKeyCrypter.deriveECKey(password);
wallet = Storage.getStorage().loadWallet(file, encryptionFullKey);
encryptionPubKey = ECKey.fromPublicOnly(encryptionFullKey);
} else {
throw new IOException("Unsupported file type");
}
if(wallet.containsSeeds()) {
//Derive xpub and master fingerprint from seed, potentially with passphrase
Wallet copy = wallet.copy();
if(wallet.isEncrypted()) {
if(password == null) {
throw new IllegalStateException("Wallet seeds are encrypted but wallet is not");
}
copy.decrypt(password);
}
for(Keystore copyKeystore : copy.getKeystores()) {
if(copyKeystore.hasSeed()) {
if(copyKeystore.getSeed().needsPassphrase()) {
KeystorePassphraseDialog passphraseDialog = new KeystorePassphraseDialog(copyKeystore);
Optional<String> optionalPassphrase = passphraseDialog.showAndWait();
if(optionalPassphrase.isPresent()) {
copyKeystore.getSeed().setPassphrase(optionalPassphrase.get());
} else {
return;
}
} else {
copyKeystore.getSeed().setPassphrase("");
}
}
}
for(int i = 0; i < wallet.getKeystores().size(); i++) {
Keystore keystore = wallet.getKeystores().get(i);
if(keystore.hasSeed()) {
Keystore copyKeystore = copy.getKeystores().get(i);
Keystore derivedKeystore = Keystore.fromSeed(copyKeystore.getSeed(), copyKeystore.getKeyDerivation().getDerivation());
keystore.setKeyDerivation(derivedKeystore.getKeyDerivation());
keystore.setExtendedPublicKey(derivedKeystore.getExtendedPublicKey());
keystore.getSeed().setPassphrase(copyKeystore.getSeed().getPassphrase());
}
}
}
Tab tab = addWalletTab(file, encryptionPubKey, wallet);
tabs.getSelectionModel().select(tab);
} catch (InvalidPasswordException e) {
@@ -55,19 +55,7 @@ public class FileWalletExportPane extends TitledDescriptionPane {
WalletPasswordDialog dlg = new WalletPasswordDialog(WalletPasswordDialog.PasswordRequirement.LOAD);
Optional<String> password = dlg.showAndWait();
if(password.isPresent()) {
copy.decrypt(password.get(), "");
for(Keystore keystore : copy.getKeystores()) {
if(keystore.hasSeed() && keystore.getSeed().usesPassphrase()) {
KeystorePassphraseDialog passphraseDialog = new KeystorePassphraseDialog(keystore);
Optional<String> passphrase = passphraseDialog.showAndWait();
if(passphrase.isPresent()) {
keystore.getSeed().setPassphrase(passphrase.get());
} else {
return;
}
}
}
copy.decrypt(password.get());
} else {
return;
}
@@ -28,7 +28,7 @@ public class Bip39 implements KeystoreMnemonicImport {
public Keystore getKeystore(List<ChildNumber> derivation, List<String> mnemonicWords, String passphrase) throws ImportException {
try {
Bip39MnemonicCode.INSTANCE.check(mnemonicWords);
DeterministicSeed seed = new DeterministicSeed(mnemonicWords, null, passphrase, System.currentTimeMillis(), DeterministicSeed.Type.BIP39);
DeterministicSeed seed = new DeterministicSeed(mnemonicWords, passphrase, System.currentTimeMillis(), DeterministicSeed.Type.BIP39);
return Keystore.fromSeed(seed, derivation);
} catch (Exception e) {
throw new ImportException(e);
@@ -34,7 +34,7 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle
@Override
public Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
InputStreamReader reader = new InputStreamReader(inputStream);
ColdcardKeystore cck = Storage.getStorage().getGson().fromJson(reader, ColdcardKeystore.class);
ColdcardKeystore cck = Storage.getGson().fromJson(reader, ColdcardKeystore.class);
Keystore keystore = new Keystore("Coldcard " + cck.xfp);
keystore.setSource(KeystoreSource.HW_AIRGAPPED);
@@ -43,7 +43,7 @@ public class ECIESInputStream extends FilterInputStream {
byte[] encryptedBytes = ByteStreams.toByteArray(in);
in.close();
ECIESKeyCrypter keyCrypter = new ECIESKeyCrypter();
byte[] decryptedBytes = keyCrypter.decrypt(new EncryptedData(encryptionMagic, encryptedBytes), decryptionKey);
byte[] decryptedBytes = keyCrypter.decrypt(new EncryptedData(encryptionMagic, encryptedBytes, null), decryptionKey);
in = new ByteArrayInputStream(decryptedBytes);
decrypted = true;
}
@@ -112,17 +112,15 @@ public class Electrum implements KeystoreFileImport, WalletImport, WalletExport
throw new ImportException("Electrum does not support exporting BIP39 derived seeds.");
} else if(ek.seed != null) {
keystore.setSource(KeystoreSource.SW_SEED);
String seed = ek.seed;
String mnemonic = ek.seed;
String passphrase = ek.passphrase;
if(password != null) {
seed = decrypt(seed, password);
mnemonic = decrypt(mnemonic, password);
passphrase = decrypt(passphrase, password);
}
keystore.setSeed(new DeterministicSeed(seed, null, passphrase, 0, DeterministicSeed.Type.ELECTRUM));
keystore.getSeed().setPassphrase(passphrase);
if(derivationPath == "m/0") {
keystore.setSeed(new DeterministicSeed(mnemonic, passphrase, 0, DeterministicSeed.Type.ELECTRUM));
if(derivationPath.equals("m/0")) {
derivationPath = "m/0'";
}
} else {
@@ -4,6 +4,7 @@ import com.google.gson.*;
import com.sparrowwallet.drongo.ExtendedKey;
import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.crypto.ECKey;
import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.Wallet;
import java.io.*;
@@ -20,12 +21,7 @@ public class Storage {
private final Gson gson;
private Storage() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeySerializer());
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer());
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArraySerializer());
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArrayDeserializer());
gson = gsonBuilder.setPrettyPrinting().disableHtmlEscaping().create();
gson = getGson();
}
public static Storage getStorage() {
@@ -36,8 +32,21 @@ public class Storage {
return SINGLETON;
}
public Gson getGson() {
return gson;
public static Gson getGson() {
return getGson(true);
}
private static Gson getGson(boolean includeKeystoreSerializer) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeySerializer());
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer());
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArraySerializer());
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArrayDeserializer());
if(includeKeystoreSerializer) {
gsonBuilder.registerTypeAdapter(Keystore.class, new KeystoreSerializer());
}
return gsonBuilder.setPrettyPrinting().disableHtmlEscaping().create();
}
public Wallet loadWallet(File file) throws IOException {
@@ -150,4 +159,18 @@ public class Storage {
return Utils.hexToBytes(json.getAsJsonPrimitive().getAsString());
}
}
private static class KeystoreSerializer implements JsonSerializer<Keystore> {
@Override
public JsonElement serialize(Keystore keystore, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = (JsonObject)getGson(false).toJsonTree(keystore);
if(keystore.hasSeed()) {
jsonObject.remove("extendedPublicKey");
jsonObject.getAsJsonObject("keyDerivation").remove("masterFingerprint");
}
return jsonObject;
}
}
}
@@ -152,7 +152,6 @@ public class KeystoreController extends WalletFormController implements Initiali
type.setText(getTypeLabel(keystore));
boolean editable = (keystore.getSource() == KeystoreSource.SW_WATCH);
label.setEditable(editable);
fingerprint.setEditable(editable);
derivation.setEditable(editable);
xpub.setEditable(editable);