mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 13:16:44 +00:00
add wallet import for samourai backup export
This commit is contained in:
parent
31346e2afa
commit
d68ab40c94
7 changed files with 105 additions and 4 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 3f4ee7af747b80976cda8ebd3c687b6a4ba5ea3f
|
Subproject commit 7584bcf26001d3705bb9467349112bc701905e7f
|
|
@ -38,6 +38,7 @@ public class FileWalletKeystoreImportPane extends FileImportPane {
|
||||||
private final KeystoreFileImport importer;
|
private final KeystoreFileImport importer;
|
||||||
private String fileName;
|
private String fileName;
|
||||||
private byte[] fileBytes;
|
private byte[] fileBytes;
|
||||||
|
private String password;
|
||||||
|
|
||||||
public FileWalletKeystoreImportPane(KeystoreFileImport importer) {
|
public FileWalletKeystoreImportPane(KeystoreFileImport importer) {
|
||||||
super(importer, importer.getName(), "Wallet import", importer.getKeystoreImportDescription(), "image/" + importer.getWalletModel().getType() + ".png", importer.isKeystoreImportScannable(), importer.isFileFormatAvailable());
|
super(importer, importer.getName(), "Wallet import", importer.getKeystoreImportDescription(), "image/" + importer.getWalletModel().getType() + ".png", importer.isKeystoreImportScannable(), importer.isFileFormatAvailable());
|
||||||
|
@ -46,6 +47,7 @@ public class FileWalletKeystoreImportPane extends FileImportPane {
|
||||||
|
|
||||||
protected void importFile(String fileName, InputStream inputStream, String password) throws ImportException {
|
protected void importFile(String fileName, InputStream inputStream, String password) throws ImportException {
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
|
this.password = password;
|
||||||
|
|
||||||
List<ScriptType> scriptTypes = ScriptType.getAddressableScriptTypes(PolicyType.SINGLE);
|
List<ScriptType> scriptTypes = ScriptType.getAddressableScriptTypes(PolicyType.SINGLE);
|
||||||
if(wallets != null && !wallets.isEmpty()) {
|
if(wallets != null && !wallets.isEmpty()) {
|
||||||
|
@ -83,7 +85,7 @@ public class FileWalletKeystoreImportPane extends FileImportPane {
|
||||||
EventManager.get().post(new WalletImportEvent(wallet));
|
EventManager.get().post(new WalletImportEvent(wallet));
|
||||||
} else {
|
} else {
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes);
|
ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes);
|
||||||
Keystore keystore = importer.getKeystore(scriptType, bais, "");
|
Keystore keystore = importer.getKeystore(scriptType, bais, password);
|
||||||
|
|
||||||
Wallet wallet = new Wallet();
|
Wallet wallet = new Wallet();
|
||||||
wallet.setName(Files.getNameWithoutExtension(fileName));
|
wallet.setName(Files.getNameWithoutExtension(fileName));
|
||||||
|
|
|
@ -51,7 +51,8 @@ public class WalletImportDialog extends Dialog<Wallet> {
|
||||||
AnchorPane.setRightAnchor(scrollPane, 0.0);
|
AnchorPane.setRightAnchor(scrollPane, 0.0);
|
||||||
|
|
||||||
importAccordion = new Accordion();
|
importAccordion = new Accordion();
|
||||||
List<KeystoreFileImport> keystoreImporters = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig(), new Jade(), new KeystoneSinglesig(), new PassportSinglesig(), new GordianSeedTool(), new SeedSigner(), new SpecterDIY(), new Krux(), new AirGapVault());
|
List<KeystoreFileImport> keystoreImporters = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig(), new Jade(), new KeystoneSinglesig(), new PassportSinglesig(),
|
||||||
|
new GordianSeedTool(), new SeedSigner(), new SpecterDIY(), new Krux(), new AirGapVault(), new Samourai());
|
||||||
for(KeystoreFileImport importer : keystoreImporters) {
|
for(KeystoreFileImport importer : keystoreImporters) {
|
||||||
if(!importer.isDeprecated() || Config.get().isShowDeprecatedImportExport()) {
|
if(!importer.isDeprecated() || Config.get().isShowDeprecatedImportExport()) {
|
||||||
FileWalletKeystoreImportPane importPane = new FileWalletKeystoreImportPane(importer);
|
FileWalletKeystoreImportPane importPane = new FileWalletKeystoreImportPane(importer);
|
||||||
|
@ -59,7 +60,8 @@ public class WalletImportDialog extends Dialog<Wallet> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WalletImport> walletImporters = new ArrayList<>(List.of(new Bip129(), new CaravanMultisig(), new ColdcardMultisig(), new CoboVaultMultisig(), new Electrum(), new KeystoneMultisig(), new Descriptor(), new SpecterDesktop(), new BlueWalletMultisig(), new Sparrow(), new JadeMultisig()));
|
List<WalletImport> walletImporters = new ArrayList<>(List.of(new Bip129(), new CaravanMultisig(), new ColdcardMultisig(), new CoboVaultMultisig(), new Electrum(),
|
||||||
|
new KeystoneMultisig(), new Descriptor(), new SpecterDesktop(), new BlueWalletMultisig(), new Sparrow(), new JadeMultisig()));
|
||||||
if(!selectedWalletForms.isEmpty()) {
|
if(!selectedWalletForms.isEmpty()) {
|
||||||
walletImporters.add(new WalletLabels(selectedWalletForms));
|
walletImporters.add(new WalletLabels(selectedWalletForms));
|
||||||
}
|
}
|
||||||
|
|
97
src/main/java/com/sparrowwallet/sparrow/io/Samourai.java
Normal file
97
src/main/java/com/sparrowwallet/sparrow/io/Samourai.java
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.samourai.wallet.crypto.AESUtil;
|
||||||
|
import com.samourai.wallet.util.CharSequenceX;
|
||||||
|
import com.sparrowwallet.drongo.Utils;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public class Samourai implements KeystoreFileImport {
|
||||||
|
@Override
|
||||||
|
public String getKeystoreImportDescription(int account) {
|
||||||
|
return "Import the wallet backup file samourai.txt exported from the Samourai app.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
|
||||||
|
try {
|
||||||
|
String input = CharStreams.toString(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Type stringStringMap = new TypeToken<Map<String, JsonElement>>() {
|
||||||
|
}.getType();
|
||||||
|
Map<String, JsonElement> map = gson.fromJson(input, stringStringMap);
|
||||||
|
|
||||||
|
String payload = input;
|
||||||
|
if(map.containsKey("payload")) {
|
||||||
|
payload = map.get("payload").getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int version = 1;
|
||||||
|
if(map.containsKey("version")) {
|
||||||
|
version = map.get("version").getAsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
String decrypted;
|
||||||
|
if(version == 1) {
|
||||||
|
decrypted = AESUtil.decrypt(payload, new CharSequenceX(password), AESUtil.DefaultPBKDF2Iterations);
|
||||||
|
} else if(version == 2) {
|
||||||
|
decrypted = AESUtil.decryptSHA256(payload, new CharSequenceX(password));
|
||||||
|
} else {
|
||||||
|
throw new ImportException("Unsupported backup version: " + version);
|
||||||
|
}
|
||||||
|
|
||||||
|
SamouraiBackup backup = gson.fromJson(decrypted, SamouraiBackup.class);
|
||||||
|
DeterministicSeed seed = new DeterministicSeed(Utils.hexToBytes(backup.wallet.seed), password, 0);
|
||||||
|
Keystore keystore = Keystore.fromSeed(seed, scriptType.getDefaultDerivation());
|
||||||
|
keystore.setLabel(getWalletModel().toDisplayString());
|
||||||
|
return keystore;
|
||||||
|
} catch(ImportException e) {
|
||||||
|
throw e;
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new ImportException("Error importing backup", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isKeystoreImportScannable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEncrypted(File file) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Samourai Backup";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WalletModel getWalletModel() {
|
||||||
|
return WalletModel.SAMOURAI;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SamouraiBackup {
|
||||||
|
public SamouraiWallet wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SamouraiWallet {
|
||||||
|
public boolean testnet;
|
||||||
|
public String seed;
|
||||||
|
public String fingerprint;
|
||||||
|
}
|
||||||
|
}
|
BIN
src/main/resources/image/samourai.png
Normal file
BIN
src/main/resources/image/samourai.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
src/main/resources/image/samourai@2x.png
Normal file
BIN
src/main/resources/image/samourai@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
BIN
src/main/resources/image/samourai@3x.png
Normal file
BIN
src/main/resources/image/samourai@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.6 KiB |
Loading…
Reference in a new issue