Search in sources :

Example 1 with ExtendedKey

use of com.sparrowwallet.drongo.ExtendedKey in project drongo by sparrowwallet.

the class PSBT method parseGlobalEntries.

private void parseGlobalEntries(List<PSBTEntry> globalEntries) throws PSBTParseException {
    PSBTEntry duplicate = findDuplicateKey(globalEntries);
    if (duplicate != null) {
        throw new PSBTParseException("Found duplicate key for PSBT global: " + Utils.bytesToHex(duplicate.getKey()));
    }
    for (PSBTEntry entry : globalEntries) {
        switch(entry.getKeyType()) {
            case PSBT_GLOBAL_UNSIGNED_TX:
                entry.checkOneByteKey();
                Transaction transaction = new Transaction(entry.getData());
                transaction.verify();
                inputs = transaction.getInputs().size();
                outputs = transaction.getOutputs().size();
                log.debug("Transaction with txid: " + transaction.getTxId() + " version " + transaction.getVersion() + " size " + transaction.getMessageSize() + " locktime " + transaction.getLocktime());
                for (TransactionInput input : transaction.getInputs()) {
                    if (input.getScriptSig().getProgram().length != 0) {
                        throw new PSBTParseException("Unsigned tx input does not have empty scriptSig");
                    }
                    log.debug(" Transaction input references txid: " + input.getOutpoint().getHash() + " vout " + input.getOutpoint().getIndex() + " with script " + input.getScriptSig());
                }
                for (TransactionOutput output : transaction.getOutputs()) {
                    try {
                        log.debug(" Transaction output value: " + output.getValue() + " to addresses " + Arrays.asList(output.getScript().getToAddresses()) + " with script hex " + Utils.bytesToHex(output.getScript().getProgram()) + " to script " + output.getScript());
                    } catch (NonStandardScriptException e) {
                        log.debug(" Transaction output value: " + output.getValue() + " with script hex " + Utils.bytesToHex(output.getScript().getProgram()) + " to script " + output.getScript());
                    }
                }
                this.transaction = transaction;
                break;
            case PSBT_GLOBAL_BIP32_PUBKEY:
                entry.checkOneBytePlusXpubKey();
                KeyDerivation keyDerivation = parseKeyDerivation(entry.getData());
                ExtendedKey pubKey = ExtendedKey.fromDescriptor(Base58.encodeChecked(entry.getKeyData()));
                this.extendedPublicKeys.put(pubKey, keyDerivation);
                log.debug("Pubkey with master fingerprint " + keyDerivation.getMasterFingerprint() + " at path " + keyDerivation.getDerivationPath() + ": " + pubKey.getExtendedKey());
                break;
            case PSBT_GLOBAL_VERSION:
                entry.checkOneByteKey();
                int version = (int) Utils.readUint32(entry.getData(), 0);
                this.version = version;
                log.debug("PSBT version: " + version);
                break;
            case PSBT_GLOBAL_PROPRIETARY:
                globalProprietary.put(Utils.bytesToHex(entry.getKeyData()), Utils.bytesToHex(entry.getData()));
                log.debug("PSBT global proprietary data: " + Utils.bytesToHex(entry.getData()));
                break;
            default:
                log.warn("PSBT global not recognized key type: " + entry.getKeyType());
        }
    }
}
Also used : KeyDerivation(com.sparrowwallet.drongo.KeyDerivation) PSBTEntry(com.sparrowwallet.drongo.psbt.PSBTEntry) ExtendedKey(com.sparrowwallet.drongo.ExtendedKey)

Example 2 with ExtendedKey

use of com.sparrowwallet.drongo.ExtendedKey in project drongo by sparrowwallet.

the class Keystore method checkKeystore.

public void checkKeystore() throws InvalidKeystoreException {
    if (label == null) {
        throw new InvalidKeystoreException("No label specified");
    }
    if (source == null) {
        throw new InvalidKeystoreException("No source specified");
    }
    if (walletModel == null) {
        throw new InvalidKeystoreException("No wallet model specified");
    }
    if (keyDerivation == null) {
        throw new InvalidKeystoreException("No key derivation specified");
    }
    if (extendedPublicKey == null) {
        throw new InvalidKeystoreException("No extended public key specified");
    }
    if (label.isEmpty()) {
        throw new InvalidKeystoreException("Label too short");
    }
    if (label.replace(" ", "").length() > MAX_LABEL_LENGTH) {
        throw new InvalidKeystoreException("Label too long");
    }
    if (keyDerivation.getDerivationPath() == null || keyDerivation.getDerivationPath().isEmpty() || !KeyDerivation.isValid(keyDerivation.getDerivationPath())) {
        throw new InvalidKeystoreException("Invalid key derivation path of " + keyDerivation.getDerivationPath());
    }
    if (keyDerivation.getMasterFingerprint() == null || keyDerivation.getMasterFingerprint().length() != 8 || !Utils.isHex(keyDerivation.getMasterFingerprint())) {
        throw new InvalidKeystoreException("Invalid master fingerprint of " + keyDerivation.getMasterFingerprint());
    }
    if (source == KeystoreSource.SW_SEED) {
        if (seed == null && masterPrivateExtendedKey == null) {
            throw new InvalidKeystoreException("Source of " + source + " but no seed or master private key is present");
        }
        if ((seed != null && !seed.isEncrypted()) || (masterPrivateExtendedKey != null && !masterPrivateExtendedKey.isEncrypted())) {
            try {
                List<ChildNumber> derivation = getKeyDerivation().getDerivation();
                DeterministicKey derivedKey = getExtendedMasterPrivateKey().getKey(derivation);
                DeterministicKey derivedKeyPublicOnly = derivedKey.dropPrivateBytes().dropParent();
                ExtendedKey xpub = new ExtendedKey(derivedKeyPublicOnly, derivedKey.getParentFingerprint(), derivation.isEmpty() ? ChildNumber.ZERO : derivation.get(derivation.size() - 1));
                if (!xpub.equals(getExtendedPublicKey())) {
                    throw new InvalidKeystoreException("Specified extended public key does not match public key derived from seed");
                }
            } catch (MnemonicException e) {
                throw new InvalidKeystoreException("Invalid mnemonic specified for seed", e);
            }
        }
    }
    if (source == KeystoreSource.SW_PAYMENT_CODE) {
        if (externalPaymentCode == null) {
            throw new InvalidKeystoreException("Source of " + source + " but no payment code is present");
        }
        if (bip47ExtendedPrivateKey == null) {
            throw new InvalidKeystoreException("Source of " + source + " but no extended private key is present");
        }
    }
}
Also used : ExtendedKey(com.sparrowwallet.drongo.ExtendedKey)

Example 3 with ExtendedKey

use of com.sparrowwallet.drongo.ExtendedKey in project drongo by sparrowwallet.

the class Keystore method getKey.

public ECKey getKey(WalletNode walletNode) throws MnemonicException {
    if (source == KeystoreSource.SW_PAYMENT_CODE) {
        try {
            if (walletNode.getKeyPurpose() != KeyPurpose.RECEIVE) {
                throw new IllegalArgumentException("Cannot get private key for non-receive chain");
            }
            PaymentAddress paymentAddress = getPaymentAddress(walletNode.getKeyPurpose(), walletNode.getIndex());
            return paymentAddress.getReceiveECKey();
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Invalid payment code " + externalPaymentCode, e);
        } catch (Exception e) {
            log.error("Cannot get receive private key at index " + walletNode.getIndex() + " for payment code " + externalPaymentCode, e);
        }
    }
    ExtendedKey extendedPrivateKey = getExtendedPrivateKey();
    List<ChildNumber> derivation = new ArrayList<>();
    derivation.add(extendedPrivateKey.getKeyChildNumber());
    derivation.addAll(walletNode.getDerivation());
    return extendedPrivateKey.getKey(derivation);
}
Also used : ArrayList(java.util.ArrayList) PaymentAddress(com.sparrowwallet.drongo.bip47.PaymentAddress) ExtendedKey(com.sparrowwallet.drongo.ExtendedKey)

Example 4 with ExtendedKey

use of com.sparrowwallet.drongo.ExtendedKey in project sparrow by sparrowwallet.

the class SparrowDataSource method getWallet.

static Wallet getWallet(String zpub) {
    return AppServices.get().getOpenWallets().keySet().stream().filter(wallet -> {
        try {
            List<ExtendedKey.Header> headers = ExtendedKey.Header.getHeaders(Network.get());
            ExtendedKey.Header header = headers.stream().filter(head -> head.getDefaultScriptType().equals(wallet.getScriptType()) && !head.isPrivateKey()).findFirst().orElse(ExtendedKey.Header.xpub);
            ExtendedKey extPubKey = wallet.getKeystores().get(0).getExtendedPublicKey();
            return extPubKey.toString(header).equals(zpub);
        } catch (Exception e) {
            return false;
        }
    }).findFirst().orElse(null);
}
Also used : ExtendedKey(com.sparrowwallet.drongo.ExtendedKey)

Example 5 with ExtendedKey

use of com.sparrowwallet.drongo.ExtendedKey in project sparrow by sparrowwallet.

the class Electrum method importWallet.

@Override
public Wallet importWallet(InputStream inputStream, String password) throws ImportException {
    Reader reader;
    if (password != null) {
        ECKey decryptionKey = Pbkdf2KeyDeriver.DEFAULT_INSTANCE.deriveECKey(password);
        reader = new InputStreamReader(new InflaterInputStream(new ECIESInputStream(inputStream, decryptionKey)), StandardCharsets.UTF_8);
    } else {
        reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
    }
    try {
        Gson gson = new Gson();
        Type stringStringMap = new TypeToken<Map<String, JsonElement>>() {
        }.getType();
        Map<String, JsonElement> map = gson.fromJson(reader, stringStringMap);
        ElectrumJsonWallet ew = new ElectrumJsonWallet();
        if (map.get("wallet_type") == null) {
            throw new ImportException("File was not a valid Electrum wallet");
        }
        ew.wallet_type = map.get("wallet_type").getAsString();
        for (String key : map.keySet()) {
            if (key.startsWith("x") || key.equals("keystore")) {
                ElectrumKeystore ek = gson.fromJson(map.get(key), ElectrumKeystore.class);
                if (ek.root_fingerprint == null && ek.ckcc_xfp != null) {
                    byte[] le = new byte[4];
                    Utils.uint32ToByteArrayLE(Long.parseLong(ek.ckcc_xfp), le, 0);
                    ek.root_fingerprint = Utils.bytesToHex(le).toUpperCase();
                }
                ew.keystores.put(key, ek);
            }
            if (key.equals("labels")) {
                JsonObject jsonObject = (JsonObject) map.get(key);
                for (String labelKey : jsonObject.keySet()) {
                    ew.labels.put(labelKey, jsonObject.get(labelKey).getAsString());
                }
            }
            if (key.equals("addresses")) {
                ew.addresses = gson.fromJson(map.get(key), ElectrumAddresses.class);
            }
            if (key.equals("verified_tx3")) {
                JsonObject jsonObject = (JsonObject) map.get(key);
                for (String txKey : jsonObject.keySet()) {
                    Sha256Hash txHash = Sha256Hash.wrap(txKey);
                    JsonArray array = jsonObject.getAsJsonArray(txKey);
                    if (array != null && array.size() > 3) {
                        int height = array.get(0).getAsInt();
                        Date date = new Date(array.get(1).getAsLong() * 1000);
                        long fee = array.get(2).getAsLong();
                        Sha256Hash blockHash = Sha256Hash.wrap(array.get(3).getAsString());
                        JsonObject transactions = (JsonObject) map.get("transactions");
                        if (transactions != null) {
                            String txhex = transactions.get(txKey).getAsString();
                            if (txhex != null) {
                                Transaction transaction = new Transaction(Utils.hexToBytes(txhex));
                                BlockTransaction blockTransaction = new BlockTransaction(txHash, height, date, fee, transaction, blockHash);
                                ew.transactions.put(txHash, blockTransaction);
                            }
                        }
                    }
                }
            }
        }
        Wallet wallet = new Wallet();
        ScriptType scriptType = null;
        for (ElectrumKeystore ek : ew.keystores.values()) {
            Keystore keystore = new Keystore();
            ExtendedKey xPub = ExtendedKey.fromDescriptor(ek.xpub);
            String derivationPath = ek.derivation;
            if (derivationPath == null) {
                derivationPath = "m/0";
            }
            String masterFingerprint = ek.root_fingerprint;
            if (masterFingerprint == null) {
                masterFingerprint = Utils.bytesToHex(xPub.getParentFingerprint());
            }
            if ("hardware".equals(ek.type)) {
                keystore.setSource(KeystoreSource.HW_USB);
                keystore.setWalletModel(WalletModel.fromType(ek.hw_type));
                if (keystore.getWalletModel() == null) {
                    throw new ImportException("Wallet has keystore of unknown hardware wallet type \"" + ek.hw_type + "\".");
                }
                if (keystore.getWalletModel().equals(WalletModel.TREZOR_1)) {
                    keystore.setWalletModel(WalletModel.TREZOR_T);
                }
            } else if ("bip32".equals(ek.type)) {
                if (ek.xprv != null && ek.seed == null) {
                    throw new ImportException("Electrum does not support exporting BIP39 derived seeds, as it does not store the mnemonic words. Only seeds created with its native Electrum Seed Version System are exportable. " + "If you have the mnemonic words, create a new wallet with a BIP39 keystore.");
                } else if (ek.seed != null) {
                    keystore.setSource(KeystoreSource.SW_SEED);
                    String mnemonic = ek.seed;
                    String passphrase = ek.passphrase;
                    if (password != null) {
                        mnemonic = decrypt(mnemonic, password);
                        passphrase = decrypt(passphrase, password);
                    }
                    keystore.setSeed(new DeterministicSeed(mnemonic, passphrase, 0, DeterministicSeed.Type.ELECTRUM));
                    // Ensure the derivation path from the seed matches the provided xpub
                    String[] possibleDerivations = { "m", "m/0", "m/0'" };
                    for (String possibleDerivation : possibleDerivations) {
                        List<ChildNumber> derivation = KeyDerivation.parsePath(possibleDerivation);
                        DeterministicKey derivedKey = keystore.getExtendedMasterPrivateKey().getKey(derivation);
                        DeterministicKey derivedKeyPublicOnly = derivedKey.dropPrivateBytes().dropParent();
                        ExtendedKey xpub = new ExtendedKey(derivedKeyPublicOnly, derivedKey.getParentFingerprint(), derivation.isEmpty() ? ChildNumber.ZERO : derivation.get(derivation.size() - 1));
                        if (xpub.equals(xPub)) {
                            derivationPath = possibleDerivation;
                            break;
                        }
                    }
                } else {
                    keystore.setSource(KeystoreSource.SW_WATCH);
                }
                keystore.setWalletModel(WalletModel.ELECTRUM);
            }
            keystore.setKeyDerivation(new KeyDerivation(masterFingerprint, derivationPath));
            keystore.setExtendedPublicKey(xPub);
            keystore.setLabel(ek.label != null ? ek.label : "Electrum");
            if (keystore.getLabel().length() > Keystore.MAX_LABEL_LENGTH) {
                keystore.setLabel(keystore.getLabel().substring(0, Keystore.MAX_LABEL_LENGTH));
            }
            wallet.getKeystores().add(keystore);
            ExtendedKey.Header xpubHeader = ExtendedKey.Header.fromExtendedKey(ek.xpub);
            scriptType = xpubHeader.getDefaultScriptType();
        }
        wallet.setScriptType(scriptType);
        if (ew.wallet_type.equals("standard")) {
            wallet.setPolicyType(PolicyType.SINGLE);
            wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, scriptType, wallet.getKeystores(), 1));
        } else if (ew.wallet_type.contains("of")) {
            wallet.setPolicyType(PolicyType.MULTI);
            String[] mOfn = ew.wallet_type.split("of");
            int threshold = Integer.parseInt(mOfn[0]);
            wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.MULTI, scriptType, wallet.getKeystores(), threshold));
        } else {
            throw new ImportException("Unknown Electrum wallet type of " + ew.wallet_type);
        }
        for (String key : ew.labels.keySet()) {
            try {
                Sha256Hash txHash = Sha256Hash.wrap(key);
                BlockTransaction blockTransaction = ew.transactions.get(txHash);
                if (blockTransaction != null) {
                    blockTransaction.setLabel(ew.labels.get(key));
                }
            } catch (Exception e) {
                // not a tx - try an address
                if (ew.addresses != null) {
                    try {
                        Address address = Address.fromString(key);
                        Map<KeyPurpose, List<String>> keyPurposes = Map.of(KeyPurpose.RECEIVE, ew.addresses.receiving, KeyPurpose.CHANGE, ew.addresses.change);
                        for (KeyPurpose keyPurpose : keyPurposes.keySet()) {
                            WalletNode purposeNode = wallet.getNode(keyPurpose);
                            purposeNode.fillToIndex(keyPurposes.get(keyPurpose).size() - 1);
                            for (WalletNode addressNode : purposeNode.getChildren()) {
                                if (address.equals(addressNode.getAddress())) {
                                    addressNode.setLabel(ew.labels.get(key));
                                }
                            }
                        }
                        for (BlockTransaction blkTx : ew.transactions.values()) {
                            if (blkTx.getLabel() == null) {
                                Transaction tx = blkTx.getTransaction();
                                for (TransactionOutput txOutput : tx.getOutputs()) {
                                    try {
                                        Address[] addresses = txOutput.getScript().getToAddresses();
                                        if (Arrays.asList(addresses).contains(address)) {
                                            blkTx.setLabel(ew.labels.get(key));
                                        }
                                    } catch (NonStandardScriptException ex) {
                                    // ignore
                                    }
                                }
                            }
                        }
                    } catch (Exception ex) {
                    // not an address
                    }
                }
            }
        }
        wallet.updateTransactions(ew.transactions);
        try {
            wallet.checkWallet();
        } catch (InvalidWalletException e) {
            throw new IllegalStateException("Imported Electrum wallet was invalid: " + e.getMessage());
        }
        return wallet;
    } catch (Exception e) {
        throw new ImportException("Error importing Electrum Wallet", e);
    }
}
Also used : Address(com.sparrowwallet.drongo.address.Address) KeyPurpose(com.sparrowwallet.drongo.KeyPurpose) InflaterInputStream(java.util.zip.InflaterInputStream) KeyDerivation(com.sparrowwallet.drongo.KeyDerivation) PolicyType(com.sparrowwallet.drongo.policy.PolicyType) Type(java.lang.reflect.Type) ExtendedKey(com.sparrowwallet.drongo.ExtendedKey)

Aggregations

ExtendedKey (com.sparrowwallet.drongo.ExtendedKey)10 KeyDerivation (com.sparrowwallet.drongo.KeyDerivation)6 KeyPurpose (com.sparrowwallet.drongo.KeyPurpose)2 PaymentAddress (com.sparrowwallet.drongo.bip47.PaymentAddress)2 PolicyType (com.sparrowwallet.drongo.policy.PolicyType)2 ArrayList (java.util.ArrayList)2 Test (org.junit.Test)2 OutputDescriptor (com.sparrowwallet.drongo.OutputDescriptor)1 Utils (com.sparrowwallet.drongo.Utils)1 Address (com.sparrowwallet.drongo.address.Address)1 PaymentCode (com.sparrowwallet.drongo.bip47.PaymentCode)1 com.sparrowwallet.drongo.crypto (com.sparrowwallet.drongo.crypto)1 ChildNumber (com.sparrowwallet.drongo.crypto.ChildNumber)1 ScriptType (com.sparrowwallet.drongo.protocol.ScriptType)1 PSBTEntry (com.sparrowwallet.drongo.psbt.PSBTEntry)1 BufferedReader (java.io.BufferedReader)1 InputStreamReader (java.io.InputStreamReader)1 Type (java.lang.reflect.Type)1 List (java.util.List)1 InflaterInputStream (java.util.zip.InflaterInputStream)1