/** * Test with random plain text strings and random passwords. UUIDs are used and hence will only * cover hex characters (and the separator hyphen). * * @throws KeyCrypterException * @throws UnsupportedEncodingException */ @Test public void testKeyCrypterGood2() throws Exception { KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); System.out.print("EncrypterDecrypterTest: Trying random UUIDs for plainText and passwords :"); int numberOfTests = 16; for (int i = 0; i < numberOfTests; i++) { // Create a UUID as the plaintext and use another for the password. String plainText = UUID.randomUUID().toString(); CharSequence password = UUID.randomUUID().toString(); EncryptedPrivateKey encryptedPrivateKey = keyCrypter.encrypt(plainText.getBytes(), keyCrypter.deriveKey(password)); assertNotNull(encryptedPrivateKey); byte[] reconstructedPlainBytes = keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(password)); assertEquals( Utils.bytesToHexString(plainText.getBytes()), Utils.bytesToHexString(reconstructedPlainBytes)); System.out.print('.'); } System.out.println(" Done."); }
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(); } }
@Test public void deterministicUpgradeEncrypted() throws Exception { group = new KeyChainGroup(params); final ECKey key = new ECKey(); group.importKeys(key); final KeyCrypterScrypt crypter = new KeyCrypterScrypt(); final KeyParameter aesKey = crypter.deriveKey("abc"); assertTrue(group.isDeterministicUpgradeRequired()); group.encrypt(crypter, aesKey); assertTrue(group.isDeterministicUpgradeRequired()); try { group.upgradeToDeterministic(0, null); fail(); } catch (DeterministicUpgradeRequiresPassword e) { // Expected. } group.upgradeToDeterministic(0, aesKey); assertFalse(group.isDeterministicUpgradeRequired()); final DeterministicSeed deterministicSeed = group.getActiveKeyChain().getSeed(); assertNotNull(deterministicSeed); assertTrue(deterministicSeed.isEncrypted()); byte[] entropy = checkNotNull(group.getActiveKeyChain().toDecrypted(aesKey).getSeed()).getEntropyBytes(); // Check we used the right key: oldest non rotating. byte[] truncatedBytes = Arrays.copyOfRange(key.getSecretBytes(), 0, 16); assertArrayEquals(entropy, truncatedBytes); }
@Test public void testEncryptDecryptBytes2() throws KeyCrypterException { KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); // Encrypt random bytes of various lengths up to length 50. Random random = new Random(); for (int i = 0; i < 50; i++) { byte[] plainBytes = new byte[i]; random.nextBytes(plainBytes); EncryptedPrivateKey encryptedPrivateKey = keyCrypter.encrypt(plainBytes, keyCrypter.deriveKey(PASSWORD1)); assertNotNull(encryptedPrivateKey); // log.debug("\nEncrypterDecrypterTest: cipherBytes = \nlength = " + cipherBytes.length + // "\n---------------\n" + Utils.bytesToHexString(cipherBytes) + "\n---------------\n"); byte[] rebornPlainBytes = keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(PASSWORD1)); log.debug("Original: (" + i + ") " + Utils.bytesToHexString(plainBytes)); log.debug("Reborn1 : (" + i + ") " + Utils.bytesToHexString(rebornPlainBytes)); assertEquals(Utils.bytesToHexString(plainBytes), Utils.bytesToHexString(rebornPlainBytes)); } }
@Test public void encryptionWhilstEmpty() throws Exception { group = new KeyChainGroup(params); group.setLookaheadSize(5); KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2); final KeyParameter aesKey = scrypt.deriveKey("password"); group.encrypt(scrypt, aesKey); assertTrue(group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).isEncrypted()); final ECKey key = group.currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); group.decrypt(aesKey); assertFalse(checkNotNull(group.findKeyFromPubKey(key.getPubKey())).isEncrypted()); }
@Test public void testKeyCrypterGood1() throws KeyCrypterException { KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); // Encrypt. EncryptedPrivateKey encryptedPrivateKey = keyCrypter.encrypt(TEST_BYTES1, keyCrypter.deriveKey(PASSWORD1)); assertNotNull(encryptedPrivateKey); // Decrypt. byte[] reborn = keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(PASSWORD1)); log.debug("Original: " + Utils.bytesToHexString(TEST_BYTES1)); log.debug("Reborn : " + Utils.bytesToHexString(reborn)); assertEquals(Utils.bytesToHexString(TEST_BYTES1), Utils.bytesToHexString(reborn)); }
@Test public void serialization() throws Exception { assertEquals(INITIAL_KEYS + 1 /* for the seed */, group.serializeToProtobuf().size()); group = KeyChainGroup.fromProtobufUnencrypted(params, group.serializeToProtobuf()); group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); DeterministicKey key1 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); DeterministicKey key2 = group.freshKey(KeyChain.KeyPurpose.CHANGE); group.getBloomFilterElementCount(); List<Protos.Key> protoKeys1 = group.serializeToProtobuf(); assertEquals( INITIAL_KEYS + ((LOOKAHEAD_SIZE + 1) * 2) + 1 /* for the seed */ + 1, protoKeys1.size()); group.importKeys(new ECKey()); List<Protos.Key> protoKeys2 = group.serializeToProtobuf(); assertEquals( INITIAL_KEYS + ((LOOKAHEAD_SIZE + 1) * 2) + 1 /* for the seed */ + 2, protoKeys2.size()); group = KeyChainGroup.fromProtobufUnencrypted(params, protoKeys1); assertEquals( INITIAL_KEYS + ((LOOKAHEAD_SIZE + 1) * 2) + 1 /* for the seed */ + 1, protoKeys1.size()); assertTrue(group.hasKey(key1)); assertTrue(group.hasKey(key2)); assertEquals(key2, group.currentKey(KeyChain.KeyPurpose.CHANGE)); assertEquals(key1, group.currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS)); group = KeyChainGroup.fromProtobufUnencrypted(params, protoKeys2); assertEquals( INITIAL_KEYS + ((LOOKAHEAD_SIZE + 1) * 2) + 1 /* for the seed */ + 2, protoKeys2.size()); assertTrue(group.hasKey(key1)); assertTrue(group.hasKey(key2)); KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2); final KeyParameter aesKey = scrypt.deriveKey("password"); group.encrypt(scrypt, aesKey); List<Protos.Key> protoKeys3 = group.serializeToProtobuf(); group = KeyChainGroup.fromProtobufEncrypted(params, protoKeys3, scrypt); assertTrue(group.isEncrypted()); assertTrue(group.checkPassword("password")); group.decrypt(aesKey); // No need for extensive contents testing here, as that's done in the keychain class tests. }
@Test public void testEncryptDecryptBytes1() throws KeyCrypterException { KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); // Encrypt bytes. EncryptedPrivateKey encryptedPrivateKey = keyCrypter.encrypt(TEST_BYTES1, keyCrypter.deriveKey(PASSWORD1)); assertNotNull(encryptedPrivateKey); log.debug( "\nEncrypterDecrypterTest: cipherBytes = \nlength = " + encryptedPrivateKey.getEncryptedBytes().length + "\n---------------\n" + Utils.bytesToHexString(encryptedPrivateKey.getEncryptedBytes()) + "\n---------------\n"); byte[] rebornPlainBytes = keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(PASSWORD1)); log.debug("Original: " + Utils.bytesToHexString(TEST_BYTES1)); log.debug("Reborn1 : " + Utils.bytesToHexString(rebornPlainBytes)); assertEquals(Utils.bytesToHexString(TEST_BYTES1), Utils.bytesToHexString(rebornPlainBytes)); }
@Test public void testKeyCrypterWrongPassword() throws KeyCrypterException { KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); // create a longer encryption string StringBuilder stringBuffer = new StringBuilder(); for (int i = 0; i < 100; i++) { stringBuffer.append(i).append(" ").append("The quick brown fox"); } EncryptedPrivateKey encryptedPrivateKey = keyCrypter.encrypt(stringBuffer.toString().getBytes(), keyCrypter.deriveKey(PASSWORD2)); assertNotNull(encryptedPrivateKey); try { keyCrypter.decrypt(encryptedPrivateKey, keyCrypter.deriveKey(WRONG_PASSWORD)); // TODO: This test sometimes fails due to relying on padding. fail("Decrypt with wrong password did not throw exception"); } catch (KeyCrypterException ede) { assertTrue(ede.getMessage().contains("Could not decrypt")); } }
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(); } }
public void encryption(boolean withImported) throws Exception { Utils.rollMockClock(0); long now = Utils.currentTimeSeconds(); ECKey a = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); assertEquals(now, group.getEarliestKeyCreationTime()); Utils.rollMockClock(-86400); long yesterday = Utils.currentTimeSeconds(); ECKey b = new ECKey(); assertFalse(group.isEncrypted()); try { group.checkPassword("foo"); // Cannot check password of an unencrypted group. fail(); } catch (IllegalStateException e) { } if (withImported) { assertEquals(now, group.getEarliestKeyCreationTime()); group.importKeys(b); assertEquals(yesterday, group.getEarliestKeyCreationTime()); } KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2); final KeyParameter aesKey = scrypt.deriveKey("password"); group.encrypt(scrypt, aesKey); assertTrue(group.isEncrypted()); assertTrue(group.checkPassword("password")); assertFalse(group.checkPassword("wrong password")); final ECKey ea = group.findKeyFromPubKey(a.getPubKey()); assertTrue(checkNotNull(ea).isEncrypted()); if (withImported) { assertTrue(checkNotNull(group.findKeyFromPubKey(b.getPubKey())).isEncrypted()); assertEquals(yesterday, group.getEarliestKeyCreationTime()); } else { assertEquals(now, group.getEarliestKeyCreationTime()); } try { ea.sign(Sha256Hash.ZERO_HASH); fail(); } catch (ECKey.KeyIsEncryptedException e) { // Ignored. } if (withImported) { ECKey c = new ECKey(); try { group.importKeys(c); fail(); } catch (KeyCrypterException e) { } group.importKeysAndEncrypt(ImmutableList.of(c), aesKey); ECKey ec = group.findKeyFromPubKey(c.getPubKey()); try { group.importKeysAndEncrypt(ImmutableList.of(ec), aesKey); fail(); } catch (IllegalArgumentException e) { } } try { group.decrypt(scrypt.deriveKey("WRONG PASSWORD")); fail(); } catch (KeyCrypterException e) { } group.decrypt(aesKey); assertFalse(group.isEncrypted()); assertFalse(checkNotNull(group.findKeyFromPubKey(a.getPubKey())).isEncrypted()); if (withImported) { assertFalse(checkNotNull(group.findKeyFromPubKey(b.getPubKey())).isEncrypted()); assertEquals(yesterday, group.getEarliestKeyCreationTime()); } else { assertEquals(now, group.getEarliestKeyCreationTime()); } }