@Override public SimpleHDKeyChain toEncrypted(KeyCrypter keyCrypter, KeyParameter aesKey) { return new SimpleHDKeyChain(keyCrypter, aesKey, this); }
@Override public boolean checkPassword(CharSequence password) { checkNotNull(password,"Password is null"); checkState(getKeyCrypter() != null, "Key chain not encrypted"); return checkAESKey(getKeyCrypter().deriveKey(password)); }
/** * Locates a keypair from the basicKeyChain given the hash of the public key. This is needed * when finding out which key we need to use to redeem a transaction output. * * @return ECKey object or null if no such key was found. */ @Nullable @Override public ECKey findKeyFromPubHash(byte[] pubkeyHash) { lock.lock(); try { return keys.findKeyFromPubHash(pubkeyHash); } finally { lock.unlock(); } }
@Override public SimpleHDKeyChain toDecrypted(CharSequence password) { checkNotNull(password, "Attempt to decrypt with a null password."); checkArgument(password.length() > 0, "Attempt to decrypt with an empty password."); KeyCrypter crypter = getKeyCrypter(); checkState(crypter != null, "Chain not encrypted"); KeyParameter derivedKey = crypter.deriveKey(password); return toDecrypted(derivedKey); }
public void serializeUnencrypted(SimpleHDKeyChain keyChain, String expectedSerialization) throws UnreadableWalletException { keyChain.setLookaheadSize(10); keyChain.maybeLookAhead(); DeterministicKey key1 = keyChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); DeterministicKey key2 = keyChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); DeterministicKey key3 = keyChain.getKey(KeyChain.KeyPurpose.CHANGE); List<Protos.Key> keys = keyChain.toProtobuf(); + (keyChain.getLookaheadSize() + keyChain.getLookaheadThreshold()) * 2 // lookahead zone on each chain DeterministicKey key4 = keyChain.getKey(KeyChain.KeyPurpose.CHANGE); int oldLookaheadSize = keyChain.getLookaheadSize(); keyChain = SimpleHDKeyChain.fromProtobuf(keys, null); assertEquals(expectedSerialization, protoToString(keyChain.toProtobuf())); assertEquals(key1, keyChain.findKeyFromPubHash(key1.getPubKeyHash())); assertEquals(key2, keyChain.findKeyFromPubHash(key2.getPubKeyHash())); assertEquals(key3, keyChain.findKeyFromPubHash(key3.getPubKeyHash())); assertEquals(key4, keyChain.getKey(KeyChain.KeyPurpose.CHANGE)); key1.sign(Sha256Hash.ZERO_HASH); key2.sign(Sha256Hash.ZERO_HASH); key3.sign(Sha256Hash.ZERO_HASH); key4.sign(Sha256Hash.ZERO_HASH); assertEquals(oldLookaheadSize, keyChain.getLookaheadSize());
public void encryption(SimpleHDKeyChain unencChain) throws UnreadableWalletException { DeterministicKey key1 = unencChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); SimpleHDKeyChain encChain = unencChain.toEncrypted("open secret"); DeterministicKey encKey1 = encChain.findKeyFromPubKey(key1.getPubKey()); checkEncryptedKeyChain(encChain, key1); // Round-trip to ensure de/serialization works and that we can store two chains and they both deserialize. List<Protos.Key> serialized = encChain.toProtobuf(); System.out.println(protoToString(serialized)); encChain = SimpleHDKeyChain.fromProtobuf(serialized, encChain.getKeyCrypter()); checkEncryptedKeyChain(encChain, unencChain.findKeyFromPubKey(key1.getPubKey())); DeterministicKey encKey2 = encChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); // Decrypt and check the keys match. SimpleHDKeyChain decChain = encChain.toDecrypted("open secret"); DeterministicKey decKey1 = decChain.findKeyFromPubHash(encKey1.getPubKeyHash()); DeterministicKey decKey2 = decChain.findKeyFromPubHash(encKey2.getPubKeyHash()); assertEquals(decKey1.getPubKeyPoint(), encKey1.getPubKeyPoint()); assertEquals(decKey2.getPubKeyPoint(), encKey2.getPubKeyPoint()); assertFalse(decKey1.isEncrypted()); assertFalse(decKey2.isEncrypted()); assertNotEquals(encKey1.getParent(), decKey1.getParent()); // parts of a different hierarchy // Check we can once again derive keys from the decrypted chain. decChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).sign(Sha256Hash.ZERO_HASH); decChain.getKey(KeyChain.KeyPurpose.CHANGE).sign(Sha256Hash.ZERO_HASH); }
chain.addEventListener(new AbstractKeyChainEventListener() { @Override public void onKeysAdded(List<ECKey> keys) { chain.setLookaheadSize(5); assertEquals(0, listenerKeys.size()); ECKey key = chain.getKey(SimpleHDKeyChain.KeyPurpose.CHANGE); assertEquals(1, listenerKeys.size()); // 1 event final List<ECKey> firstEvent = listenerKeys.get(0); chain.maybeLookAhead(); final List<ECKey> secondEvent = listenerKeys.get(0); assertEquals(12, secondEvent.size()); // (5 lookahead keys, +1 lookahead threshold) * 2 chains listenerKeys.clear(); chain.getKey(SimpleHDKeyChain.KeyPurpose.CHANGE); final int lookaheadThreshold = chain.getLookaheadThreshold() + chain.getLookaheadSize(); for (int i = 0; i < lookaheadThreshold; i++) chain.getKey(SimpleHDKeyChain.KeyPurpose.CHANGE); assertEquals(1, listenerKeys.size()); // 1 event assertEquals(1, listenerKeys.get(0).size()); // 1 key.
private void checkEncryptedKeyChain(SimpleHDKeyChain encChain, DeterministicKey key1) { // Check we can look keys up and extend the chain without the AES key being provided. DeterministicKey encKey1 = encChain.findKeyFromPubKey(key1.getPubKey()); DeterministicKey encKey2 = encChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); assertFalse(key1.isEncrypted()); assertTrue(encKey1.isEncrypted()); assertEquals(encKey1.getPubKeyPoint(), key1.getPubKeyPoint()); final KeyParameter aesKey = checkNotNull(encChain.getKeyCrypter()).deriveKey("open secret"); encKey1.sign(Sha256Hash.ZERO_HASH, aesKey); encKey2.sign(Sha256Hash.ZERO_HASH, aesKey); assertTrue(encChain.checkAESKey(aesKey)); assertFalse(encChain.checkPassword("access denied")); assertTrue(encChain.checkPassword("open secret")); }
@Test public void deriveCoin() throws Exception { DeterministicHierarchy hierarchy = new DeterministicHierarchy(masterKey); DeterministicKey rootKey = hierarchy.get(BitcoinMain.get().getBip44Path(0), false, true); chain = new SimpleHDKeyChain(rootKey); ECKey key1 = chain.getKey(SimpleHDKeyChain.KeyPurpose.RECEIVE_FUNDS); ECKey key2 = chain.getKey(SimpleHDKeyChain.KeyPurpose.RECEIVE_FUNDS); final Address address = new Address(BitcoinMain.get(), "1Fp7CA7ZVqZNFVNQ9TpeqWUas7K28K9zig"); assertEquals(address, key1.toAddress(BitcoinMain.get())); assertEquals("1AKqkQM4VqyVis6hscj8695WHPCCzgHNY3", key2.toAddress(BitcoinMain.get()).toString()); assertEquals(key1, chain.findKeyFromPubHash(address.getHash160())); assertEquals(key2, chain.findKeyFromPubKey(key2.getPubKey())); key1.sign(Sha256Hash.ZERO_HASH); ECKey key3 = chain.getKey(SimpleHDKeyChain.KeyPurpose.CHANGE); assertEquals("18YvGiRqXKxrzB72ckfrRSizWeHgwRP94V", key3.toAddress(BitcoinMain.get()).toString()); key3.sign(Sha256Hash.ZERO_HASH); ECKey key4 = chain.getKey(SimpleHDKeyChain.KeyPurpose.CHANGE); assertEquals("1861TX2MbyPEUrxDQVWgV4Tp9991bK1zpy", key4.toAddress(BitcoinMain.get()).toString()); key4.sign(Sha256Hash.ZERO_HASH); }
/** * Returns true is it is possible to create new fresh receive addresses, false otherwise */ public boolean canCreateFreshReceiveAddress() { lock.lock(); try { DeterministicKey currentUnusedKey = keys.getCurrentUnusedKey(RECEIVE_FUNDS); int maximumKeyIndex = SimpleHDKeyChain.LOOKAHEAD - 1; // If there are used keys if (!addressesStatus.isEmpty()) { int lastUsedKeyIndex = 0; // Find the last used key index for (Map.Entry<AbstractAddress, String> entry : addressesStatus.entrySet()) { if (entry.getValue() == null) continue; DeterministicKey usedKey = keys.findKeyFromPubHash(getHash160(entry.getKey())); if (usedKey != null && keys.isExternal(usedKey) && usedKey.getChildNumber().num() > lastUsedKeyIndex) { lastUsedKeyIndex = usedKey.getChildNumber().num(); } } maximumKeyIndex = lastUsedKeyIndex + SimpleHDKeyChain.LOOKAHEAD; } log.info("Maximum key index for new key is {}", maximumKeyIndex); // If we exceeded the BIP44 look ahead threshold return currentUnusedKey.getChildNumber().num() < maximumKeyIndex; } finally { lock.unlock(); } }
@Override public SimpleHDKeyChain toDecrypted(KeyParameter aesKey) { checkState(getKeyCrypter() != null, "Key chain not encrypted"); checkState(rootKey.isEncrypted(), "Root key not encrypted"); DeterministicKey decKey = rootKey.decrypt(getKeyCrypter(), aesKey); SimpleHDKeyChain chain = new SimpleHDKeyChain(decKey); // Now double check that the keys match to catch the case where the key is wrong but padding didn't catch it. if (!chain.getWatchingKey().getPubKeyPoint().equals(getWatchingKey().getPubKeyPoint())) throw new KeyCrypterException("Provided AES key is wrong"); chain.lookaheadSize = lookaheadSize; // Now copy the (pubkey only) leaf keys across to avoid rederiving them. The private key bytes are missing // anyway so there's nothing to decrypt. for (ECKey eckey : simpleKeyChain.getKeys()) { DeterministicKey key = (DeterministicKey) eckey; if (!isLeaf(key)) continue; // Not a leaf key. checkState(key.isEncrypted(), "Key is not encrypted"); DeterministicKey parent = chain.hierarchy.get(checkNotNull(key.getParent(), "Key has null parent").getPath(), false, false); // Clone the key to the new decrypted hierarchy. key = new DeterministicKey(key.getPubOnly(), parent); chain.hierarchy.putKey(key); chain.simpleKeyChain.importKeys(key); } chain.issuedExternalKeys = issuedExternalKeys; chain.issuedInternalKeys = issuedInternalKeys; return chain; }
chain = new SimpleHDKeyChain(rootKey, crypter); chain.lookaheadSize = LAZY_CALCULATE_LOOKAHEAD; rootTreeSize = rootKey.getPath().size(); chain.setLookaheadSize(lookaheadSize); chain.maybeLookAhead(); return chain;
@Test public void derive() throws Exception { ECKey key1 = chain.getKey(SimpleHDKeyChain.KeyPurpose.RECEIVE_FUNDS); ECKey key2 = chain.getKey(SimpleHDKeyChain.KeyPurpose.RECEIVE_FUNDS); final Address address = new Address(UnitTestParams.get(), "n1bQNoEx8uhmCzzA5JPG6sFdtsUQhwiQJV"); assertEquals(address, key1.toAddress(UnitTestParams.get())); assertEquals("mnHUcqUVvrfi5kAaXJDQzBb9HsWs78b42R", key2.toAddress(UnitTestParams.get()).toString()); assertEquals(key1, chain.findKeyFromPubHash(address.getHash160())); assertEquals(key2, chain.findKeyFromPubKey(key2.getPubKey())); key1.sign(Sha256Hash.ZERO_HASH); ECKey key3 = chain.getKey(SimpleHDKeyChain.KeyPurpose.CHANGE); assertEquals("mqumHgVDqNzuXNrszBmi7A2UpmwaPMx4HQ", key3.toAddress(UnitTestParams.get()).toString()); key3.sign(Sha256Hash.ZERO_HASH); }
@Test public void serializeUnencryptedEmpty() throws Exception { pocket.maybeInitializeAllKeys(); Protos.WalletPocket walletPocketProto = pocket.toProtobuf(); WalletPocketHD newPocket = new WalletPocketProtobufSerializer().readWallet(walletPocketProto, null); assertEquals(walletPocketProto.toString(), newPocket.toProtobuf().toString()); // Issued keys assertEquals(0, newPocket.keys.getNumIssuedExternalKeys()); assertEquals(0, newPocket.keys.getNumIssuedInternalKeys()); // 20 lookahead + 20 lookahead assertEquals(40, newPocket.keys.getActiveKeys().size()); }
@Test public void fillTransactions() throws Exception { pocket.onConnection(getBlockchainConnection(DOGE)); checkUnspentOutputs(getDummyUtxoSet(), pocket); assertEquals(11000000000L, pocket.getBalance().value); // Issued keys assertEquals(18, pocket.keys.getNumIssuedExternalKeys()); assertEquals(9, pocket.keys.getNumIssuedInternalKeys()); // No addresses left to subscribe List<AbstractAddress> addressesToWatch = pocket.getAddressesToWatch(); assertEquals(0, addressesToWatch.size()); // 18 external issued + 20 lookahead + 9 external issued + 20 lookahead assertEquals(67, pocket.addressesStatus.size()); assertEquals(67, pocket.addressesSubscribed.size()); BitAddress receiveAddr = pocket.getReceiveAddress(); // This key is not issued assertEquals(18, pocket.keys.getNumIssuedExternalKeys()); assertEquals(67, pocket.addressesStatus.size()); assertEquals(67, pocket.addressesSubscribed.size()); DeterministicKey key = pocket.keys.findKeyFromPubHash(receiveAddr.getHash160()); assertNotNull(key); // 18 here is the key index, not issued keys count assertEquals(18, key.getChildNumber().num()); assertEquals(11000000000L, pocket.getBalance().value); }
@Test public void externalKeyCheck() { assertFalse(chain.isExternal(chain.getKey(KeyChain.KeyPurpose.CHANGE))); assertTrue(chain.isExternal(chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS))); }
@Test public void getLastIssuedKey() { assertNull(chain.getLastIssuedKey(KeyChain.KeyPurpose.RECEIVE_FUNDS)); assertNull(chain.getLastIssuedKey(KeyChain.KeyPurpose.CHANGE)); DeterministicKey extKey = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS); DeterministicKey intKey = chain.getKey(KeyChain.KeyPurpose.CHANGE); assertEquals(extKey, chain.getLastIssuedKey(KeyChain.KeyPurpose.RECEIVE_FUNDS)); assertEquals(intKey, chain.getLastIssuedKey(KeyChain.KeyPurpose.CHANGE)); }
@Before public void setup() { BriefLogFormatter.init(); DeterministicSeed seed = new DeterministicSeed(ENTROPY, "", 0); masterKey = HDKeyDerivation.createMasterPrivateKey(seed.getSeedBytes()); DeterministicHierarchy hierarchy = new DeterministicHierarchy(masterKey); DeterministicKey rootKey = hierarchy.get(ImmutableList.of(ChildNumber.ZERO_HARDENED), false, true); chain = new SimpleHDKeyChain(rootKey); chain.setLookaheadSize(10); }
/** * Returns leaf keys issued by this chain (including lookahead zone but no lookahead threshold) */ public List<DeterministicKey> getActiveKeys() { ImmutableList.Builder<DeterministicKey> keys = ImmutableList.builder(); for (ECKey key : getKeys(true)) { DeterministicKey dKey = (DeterministicKey) key; if (isLeaf(dKey)) { if (dKey.getParent().equals(internalKey) && dKey.getChildNumber().num() >= issuedInternalKeys + lookaheadSize) continue; if (dKey.getParent().equals(externalKey) && dKey.getChildNumber().num() >= issuedExternalKeys + lookaheadSize) continue; keys.add(dKey); } } return keys.build(); }
/** Returns a freshly derived key that has not been returned by this method before. */ @Override public DeterministicKey getKey(KeyPurpose purpose) { return getKeys(purpose, 1).get(0); }