mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-24 22:58:32 +01:00
Made some methods public in BasicKeyChain. Fixes in deterministic key generation
This commit is contained in:
parent
72b7929523
commit
5e1942f3f0
4 changed files with 56 additions and 24 deletions
|
@ -41,12 +41,6 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
* is a list of {@link ChildNumber}s.</p>
|
||||
*/
|
||||
public class DeterministicHierarchy implements Serializable {
|
||||
/**
|
||||
* Child derivation may fail (although with extremely low probability); in such case it is re-attempted.
|
||||
* This is the maximum number of re-attempts (to avoid an infinite loop in case of bugs etc.).
|
||||
*/
|
||||
private static final int MAX_CHILD_DERIVATION_ATTEMPTS = 100;
|
||||
|
||||
private final Map<ImmutableList<ChildNumber>, DeterministicKey> keys = Maps.newHashMap();
|
||||
private final ImmutableList<ChildNumber> rootPath;
|
||||
// Keep track of how many child keys each node has. This is kind of weak.
|
||||
|
@ -115,7 +109,7 @@ public class DeterministicHierarchy implements Serializable {
|
|||
public DeterministicKey deriveNextChild(ImmutableList<ChildNumber> parentPath, boolean relative, boolean createParent, boolean privateDerivation) {
|
||||
DeterministicKey parent = get(parentPath, relative, createParent);
|
||||
int nAttempts = 0;
|
||||
while (nAttempts++ < MAX_CHILD_DERIVATION_ATTEMPTS) {
|
||||
while (nAttempts++ < HDKeyDerivation.MAX_CHILD_DERIVATION_ATTEMPTS) {
|
||||
try {
|
||||
ChildNumber createChildNumber = getNextChildNumberToDerive(parent.getPath(), privateDerivation);
|
||||
return deriveChild(parent, createChildNumber);
|
||||
|
|
|
@ -37,6 +37,12 @@ public final class HDKeyDerivation {
|
|||
|
||||
private HDKeyDerivation() { }
|
||||
|
||||
/**
|
||||
* Child derivation may fail (although with extremely low probability); in such case it is re-attempted.
|
||||
* This is the maximum number of re-attempts (to avoid an infinite loop in case of bugs etc.).
|
||||
*/
|
||||
public static final int MAX_CHILD_DERIVATION_ATTEMPTS = 100;
|
||||
|
||||
private static final HMac MASTER_HMAC_SHA512 = HDUtils.createHmacSha512Digest("Bitcoin seed".getBytes());
|
||||
|
||||
/**
|
||||
|
@ -88,6 +94,25 @@ public final class HDKeyDerivation {
|
|||
return deriveChildKey(parent, new ChildNumber(childNumber));
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a key of the "extended" child number, ie. with the 0x80000000 bit specifying whether to use
|
||||
* hardened derivation or not. If derivation fails, tries a next child.
|
||||
*/
|
||||
public static DeterministicKey deriveThisOrNextChildKey(DeterministicKey parent, int childNumber) {
|
||||
int nAttempts = 0;
|
||||
ChildNumber child = new ChildNumber(childNumber);
|
||||
boolean isHardened = child.isHardened();
|
||||
while (nAttempts < MAX_CHILD_DERIVATION_ATTEMPTS) {
|
||||
try {
|
||||
child = new ChildNumber(child.num() + nAttempts, isHardened);
|
||||
return deriveChildKey(parent, child);
|
||||
} catch (HDDerivationException ignore) { }
|
||||
nAttempts++;
|
||||
}
|
||||
throw new HDDerivationException("Maximum number of child derivation attempts reached, this is probably an indication of a bug.");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HDDerivationException if private derivation is attempted for a public-only parent key, or
|
||||
* if the resulting derived key is invalid (eg. private key == 0).
|
||||
|
@ -147,20 +172,25 @@ public final class HDKeyDerivation {
|
|||
byte[] il = Arrays.copyOfRange(i, 0, 32);
|
||||
byte[] chainCode = Arrays.copyOfRange(i, 32, 64);
|
||||
BigInteger ilInt = new BigInteger(1, il);
|
||||
// TODO: Throw a specific exception here to make sure the caller iterates.
|
||||
assertLessThanN(ilInt, "Illegal derived key: I_L >= n");
|
||||
ECPoint Ki = ECKey.CURVE.getG().multiply(ilInt).add(parent.getPubKeyPoint());
|
||||
checkArgument(!Ki.equals(ECKey.CURVE.getCurve().getInfinity()),
|
||||
"Illegal derived key: derived public key equals infinity.");
|
||||
assertNonInfinity(Ki, "Illegal derived key: derived public key equals infinity.");
|
||||
return new RawKeyBytes(Ki.getEncoded(true), chainCode);
|
||||
}
|
||||
|
||||
private static void assertNonZero(BigInteger integer, String errorMessage) {
|
||||
checkArgument(!integer.equals(BigInteger.ZERO), errorMessage);
|
||||
if (integer.equals(BigInteger.ZERO))
|
||||
throw new HDDerivationException(errorMessage);
|
||||
}
|
||||
|
||||
private static void assertNonInfinity(ECPoint point, String errorMessage) {
|
||||
if (point.equals(ECKey.CURVE.getCurve().getInfinity()))
|
||||
throw new HDDerivationException(errorMessage);
|
||||
}
|
||||
|
||||
private static void assertLessThanN(BigInteger integer, String errorMessage) {
|
||||
checkArgument(integer.compareTo(ECKey.CURVE.getN()) < 0, errorMessage);
|
||||
if (integer.compareTo(ECKey.CURVE.getN()) > 0)
|
||||
throw new HDDerivationException(errorMessage);
|
||||
}
|
||||
|
||||
private static class RawKeyBytes {
|
||||
|
|
|
@ -55,7 +55,7 @@ public class BasicKeyChain implements EncryptableKeyChain {
|
|||
this(null);
|
||||
}
|
||||
|
||||
BasicKeyChain(@Nullable KeyCrypter crypter) {
|
||||
public BasicKeyChain(@Nullable KeyCrypter crypter) {
|
||||
this.keyCrypter = crypter;
|
||||
hashToKeys = new LinkedHashMap<ByteString, ECKey>();
|
||||
pubkeyToKeys = new LinkedHashMap<ByteString, ECKey>();
|
||||
|
@ -178,9 +178,14 @@ public class BasicKeyChain implements EncryptableKeyChain {
|
|||
}
|
||||
}
|
||||
|
||||
/* package */ void importKey(ECKey key) {
|
||||
/**
|
||||
* Imports a key to the key chain. If key is present in the key chain, ignore it.
|
||||
*/
|
||||
public void importKey(ECKey key) {
|
||||
lock.lock();
|
||||
try {
|
||||
checkKeyEncryptionStateMatches(key);
|
||||
if (hasKey(key)) return;
|
||||
importKeyLocked(key);
|
||||
queueOnKeysAdded(ImmutableList.of(key));
|
||||
} finally {
|
||||
|
|
|
@ -98,8 +98,8 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
private final ReentrantLock lock = Threading.lock("DeterministicKeyChain");
|
||||
|
||||
private DeterministicHierarchy hierarchy;
|
||||
private DeterministicKey rootKey;
|
||||
private DeterministicSeed seed;
|
||||
@Nullable private DeterministicKey rootKey;
|
||||
@Nullable private DeterministicSeed seed;
|
||||
|
||||
// Ignored if seed != null. Useful for watching hierarchies.
|
||||
private long creationTimeSeconds = MnemonicCode.BIP39_STANDARDISATION_TIME_SECS;
|
||||
|
@ -111,7 +111,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
// a payment request that can generate lots of addresses independently.
|
||||
public static final ImmutableList<ChildNumber> ACCOUNT_ZERO_PATH = ImmutableList.of(ChildNumber.ZERO_HARDENED);
|
||||
public static final ImmutableList<ChildNumber> EXTERNAL_PATH = ImmutableList.of(ChildNumber.ZERO_HARDENED, ChildNumber.ZERO);
|
||||
public static final ImmutableList<ChildNumber> INTERNAL_PATH = ImmutableList.of(ChildNumber.ZERO_HARDENED, new ChildNumber(1, false));
|
||||
public static final ImmutableList<ChildNumber> INTERNAL_PATH = ImmutableList.of(ChildNumber.ZERO_HARDENED, ChildNumber.ONE);
|
||||
|
||||
// We try to ensure we have at least this many keys ready and waiting to be handed out via getKey().
|
||||
// See docs for getLookaheadSize() for more info on what this is for. The -1 value means it hasn't been calculated
|
||||
|
@ -245,10 +245,10 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
rootKey = HDKeyDerivation.createMasterPrivateKey(checkNotNull(seed.getSeedBytes()));
|
||||
rootKey.setCreationTimeSeconds(seed.getCreationTimeSeconds());
|
||||
initializeHierarchyUnencrypted(rootKey);
|
||||
} else {
|
||||
// We can't initialize ourselves with just an encrypted seed, so we expected deserialization code to do the
|
||||
// rest of the setup (loading the root key).
|
||||
}
|
||||
// Else...
|
||||
// We can't initialize ourselves with just an encrypted seed, so we expected deserialization code to do the
|
||||
// rest of the setup (loading the root key).
|
||||
}
|
||||
|
||||
// For use in encryption.
|
||||
|
@ -349,7 +349,6 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
// TODO: Handle the case where the derived key is >= curve order.
|
||||
List<DeterministicKey> lookahead = maybeLookAhead(parentKey, index);
|
||||
basicKeyChain.importKeys(lookahead);
|
||||
List<DeterministicKey> keys = new ArrayList<DeterministicKey>(numberOfKeys);
|
||||
|
@ -513,8 +512,11 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
return basicKeyChain.removeEventListener(listener);
|
||||
}
|
||||
|
||||
/** Returns a list of words that represent the seed. */
|
||||
/** Returns a list of words that represent the seed or null if this chain is a watching chain. */
|
||||
@Nullable
|
||||
public List<String> getMnemonicCode() {
|
||||
if (seed == null) return null;
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
return seed.getMnemonicCode();
|
||||
|
@ -922,12 +924,13 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
List<DeterministicKey> result = new ArrayList<DeterministicKey>(needed);
|
||||
long now = System.currentTimeMillis();
|
||||
log.info("Pre-generating {} keys for {}", needed, parent.getPathAsString());
|
||||
int nextChild = numChildren;
|
||||
for (int i = 0; i < needed; i++) {
|
||||
// TODO: Handle the case where the derived key is >= curve order.
|
||||
DeterministicKey key = HDKeyDerivation.deriveChildKey(parent, numChildren + i);
|
||||
DeterministicKey key = HDKeyDerivation.deriveThisOrNextChildKey(parent, nextChild);
|
||||
key = key.getPubOnly();
|
||||
hierarchy.putKey(key);
|
||||
result.add(key);
|
||||
nextChild = key.getChildNumber().num() + 1;
|
||||
}
|
||||
log.info("Took {} msec", System.currentTimeMillis() - now);
|
||||
return result;
|
||||
|
|
Loading…
Add table
Reference in a new issue