public static void estimateKeyDerivationTime() { // This is run in the background after startup. If we haven't recorded it before, do a key // derivation to see // how long it takes. This helps us produce better progress feedback, as on Windows we don't // currently have a // native Scrypt impl and the Java version is ~3 times slower, plus it depends a lot on CPU // speed. checkGuiThread(); estimatedKeyDerivationTime = Main.instance.prefs.getExpectedKeyDerivationTime(); if (estimatedKeyDerivationTime == null) { new Thread( () -> { log.info("Doing background test key derivation"); KeyCrypterScrypt scrypt = new KeyCrypterScrypt(SCRYPT_PARAMETERS); long start = System.currentTimeMillis(); scrypt.deriveKey("test password"); long msec = System.currentTimeMillis() - start; log.info("Background test key derivation took {}msec", msec); Platform.runLater( () -> { estimatedKeyDerivationTime = Duration.ofMillis(msec); Main.instance.prefs.setExpectedKeyDerivationTime(estimatedKeyDerivationTime); }); }) .start(); } }
protected Wallet doInBackground(Bundle... params) { ArrayList<String> seedWords = new ArrayList<String>(); for (String word : seed.trim().split(" ")) { if (word.isEmpty()) continue; seedWords.add(word); } Wallet wallet = null; try { if (seedProtect) { wallet = new Wallet(seedWords, password); } else { wallet = new Wallet(seedWords); } KeyParameter aesKey = null; if (password != null && !password.isEmpty()) { KeyCrypterScrypt crypter = new KeyCrypterScrypt(); aesKey = crypter.deriveKey(password); wallet.encrypt(crypter, aesKey); } wallet.createAccounts(coinsToCreate, true, aesKey); getWalletApplication().setWallet(wallet); getWalletApplication().saveWalletNow(); getWalletApplication().startBlockchainService(CoinService.ServiceMode.RESET_WALLET); } catch (Exception e) { log.error("Error creating a wallet", e); errorMessage = e.getMessage(); } return wallet; }
@Test(expected = KeyCrypterException.class) public void cannotMixParams() throws Exception { chain = chain.toEncrypted("foobar"); KeyCrypterScrypt scrypter = new KeyCrypterScrypt(2); // Some bogus params. ECKey key1 = new ECKey().encrypt(scrypter, scrypter.deriveKey("other stuff")); chain.importKeys(key1); }
public class WalletSetPasswordController { private static final Logger log = LoggerFactory.getLogger(WalletSetPasswordController.class); public PasswordField pass1, pass2; public ProgressIndicator progressMeter; public GridPane widgetGrid; public HBox buttonHBox; public Label explanationLabel; public Main.OverlayUI overlayUI; // These params were determined empirically on a top-range (as of 2014) MacBook Pro with native // scrypt support, // using the scryptenc command line tool from the original scrypt distribution, given a memory // limit of 40mb. public static final Protos.ScryptParameters SCRYPT_PARAMETERS = Protos.ScryptParameters.newBuilder() .setP(6) .setR(8) .setN(32768) .setSalt(ByteString.copyFrom(KeyCrypterScrypt.randomSalt())) .build(); public void initialize() { progressMeter.setOpacity(0); } public static Duration estimatedKeyDerivationTime = null; public static void estimateKeyDerivationTime() { // This is run in the background after startup. If we haven't recorded it before, do a key // derivation to see // how long it takes. This helps us produce better progress feedback, as on Windows we don't // currently have a // native Scrypt impl and the Java version is ~3 times slower, plus it depends a lot on CPU // speed. checkGuiThread(); estimatedKeyDerivationTime = Main.instance.prefs.getExpectedKeyDerivationTime(); if (estimatedKeyDerivationTime == null) { new Thread( () -> { log.info("Doing background test key derivation"); KeyCrypterScrypt scrypt = new KeyCrypterScrypt(SCRYPT_PARAMETERS); long start = System.currentTimeMillis(); scrypt.deriveKey("test password"); long msec = System.currentTimeMillis() - start; log.info("Background test key derivation took {}msec", msec); Platform.runLater( () -> { estimatedKeyDerivationTime = Duration.ofMillis(msec); Main.instance.prefs.setExpectedKeyDerivationTime(estimatedKeyDerivationTime); }); }) .start(); } } @FXML public void setPasswordClicked(ActionEvent event) { if (!pass1.getText().equals(pass2.getText())) { informationalAlert(tr("Passwords do not match"), tr("Try re-typing your chosen passwords.")); return; } String password = pass1.getText(); // This is kind of arbitrary and we could do much more to help people pick strong passwords. if (password.length() < 4) { informationalAlert( tr("Password too short"), tr("You need to pick a password at least five characters or longer.")); return; } fadeIn(progressMeter); fadeOut(widgetGrid); fadeOut(explanationLabel); fadeOut(buttonHBox); KeyCrypterScrypt scrypt = new KeyCrypterScrypt(SCRYPT_PARAMETERS); // Deriving the actual key runs on a background thread. 500msec is empirical on my laptop // (actual val is more like 333 but we give padding time). KeyDerivationTasks tasks = new KeyDerivationTasks(scrypt, password, estimatedKeyDerivationTime) { @Override protected void onFinish(KeyParameter aesKey, int timeTakenMsec) { // Write the target time to the wallet so we can make the progress bar work when // entering the password. WalletPasswordController.setTargetTime(Duration.ofMillis(timeTakenMsec)); // The actual encryption part doesn't take very long as most private keys are derived on // demand. log.info("Key derived, now encrypting"); Main.bitcoin.wallet().encrypt(scrypt, aesKey); log.info("Encryption done"); informationalAlert( tr("Wallet encrypted"), tr("You can remove the password at any time from the settings screen.")); overlayUI.done(); } }; progressMeter.progressProperty().bind(tasks.progress); tasks.start(); } @FXML public void closeClicked(ActionEvent event) { overlayUI.done(); } }
/** * Converts the given wallet to the object representation of the protocol buffers. This can be * modified, or additional data fields set, before serialization takes place. */ public Protos.Wallet walletToProto(Wallet wallet) { Protos.Wallet.Builder walletBuilder = Protos.Wallet.newBuilder(); walletBuilder.setNetworkIdentifier(wallet.getNetworkParameters().getId()); if (wallet.getDescription() != null) { walletBuilder.setDescription(wallet.getDescription()); } for (WalletTransaction wtx : wallet.getWalletTransactions()) { Protos.Transaction txProto = makeTxProto(wtx); walletBuilder.addTransaction(txProto); } walletBuilder.addAllKey(wallet.serializeKeychainToProtobuf()); for (Script script : wallet.getWatchedScripts()) { Protos.Script protoScript = Protos.Script.newBuilder() .setProgram(ByteString.copyFrom(script.getProgram())) .setCreationTimestamp(script.getCreationTimeSeconds() * 1000) .build(); walletBuilder.addWatchedScript(protoScript); } // Populate the lastSeenBlockHash field. Sha256Hash lastSeenBlockHash = wallet.getLastBlockSeenHash(); if (lastSeenBlockHash != null) { walletBuilder.setLastSeenBlockHash(hashToByteString(lastSeenBlockHash)); walletBuilder.setLastSeenBlockHeight(wallet.getLastBlockSeenHeight()); } if (wallet.getLastBlockSeenTimeSecs() > 0) walletBuilder.setLastSeenBlockTimeSecs(wallet.getLastBlockSeenTimeSecs()); // Populate the scrypt parameters. KeyCrypter keyCrypter = wallet.getKeyCrypter(); if (keyCrypter == null) { // The wallet is unencrypted. walletBuilder.setEncryptionType(EncryptionType.UNENCRYPTED); } else { // The wallet is encrypted. walletBuilder.setEncryptionType(keyCrypter.getUnderstoodEncryptionType()); if (keyCrypter instanceof KeyCrypterScrypt) { KeyCrypterScrypt keyCrypterScrypt = (KeyCrypterScrypt) keyCrypter; walletBuilder.setEncryptionParameters(keyCrypterScrypt.getScryptParameters()); } else { // Some other form of encryption has been specified that we do not know how to persist. throw new RuntimeException( "The wallet has encryption of type '" + keyCrypter.getUnderstoodEncryptionType() + "' but this WalletProtobufSerializer does not know how to persist this."); } } if (wallet.getKeyRotationTime() != null) { long timeSecs = wallet.getKeyRotationTime().getTime() / 1000; walletBuilder.setKeyRotationTime(timeSecs); } populateExtensions(wallet, walletBuilder); for (Map.Entry<String, ByteString> entry : wallet.getTags().entrySet()) { Protos.Tag.Builder tag = Protos.Tag.newBuilder().setTag(entry.getKey()).setData(entry.getValue()); walletBuilder.addTags(tag); } for (TransactionSigner signer : wallet.getTransactionSigners()) { // do not serialize LocalTransactionSigner as it's being added implicitly if (signer instanceof LocalTransactionSigner) continue; Protos.TransactionSigner.Builder protoSigner = Protos.TransactionSigner.newBuilder(); protoSigner.setClassName(signer.getClass().getName()); protoSigner.setData(ByteString.copyFrom(signer.serialize())); walletBuilder.addTransactionSigners(protoSigner); } walletBuilder.setSigsRequiredToSpend(wallet.getSigsRequiredToSpend()); // Populate the wallet version. walletBuilder.setVersion(wallet.getVersion()); return walletBuilder.build(); }