Exemplo n.º 1
0
  /**
   * @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).
   */
  public static DeterministicKey deriveChildKey(DeterministicKey parent, ChildNumber childNumber)
      throws HDDerivationException {

    RawKeyBytes rawKey = deriveChildKeyBytes(parent, childNumber);
    return new DeterministicKey(
        HDUtils.append(parent.getChildNumberPath(), childNumber),
        rawKey.chainCode,
        parent.hasPrivate() ? null : HDUtils.getCurve().decodePoint(rawKey.keyBytes),
        parent.hasPrivate() ? HDUtils.toBigInteger(rawKey.keyBytes) : null,
        parent);
  }
Exemplo n.º 2
0
 public static DeterministicKey createMasterPubKeyFromBytes(byte[] pubKeyBytes, byte[] chainCode) {
   return new DeterministicKey(
       ImmutableList.<ChildNumber>of(),
       chainCode,
       HDUtils.getCurve().decodePoint(pubKeyBytes),
       null,
       null);
 }
Exemplo n.º 3
0
 /** @throws HDDerivationException if privKeyBytes is invalid (0 or >= n). */
 public static DeterministicKey createMasterPrivKeyFromBytes(byte[] privKeyBytes, byte[] chainCode)
     throws HDDerivationException {
   BigInteger privateKeyFieldElt = HDUtils.toBigInteger(privKeyBytes);
   assertNonZero(privateKeyFieldElt, "Generated master key is invalid.");
   assertLessThanN(privateKeyFieldElt, "Generated master key is invalid.");
   return new DeterministicKey(
       ImmutableList.<ChildNumber>of(), chainCode, null, privateKeyFieldElt, null);
 }
Exemplo n.º 4
0
 /**
  * @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).
  */
 public static DeterministicKey deriveChildKey(DeterministicKey parent, ChildNumber childNumber)
     throws HDDerivationException {
   if (parent.isPubKeyOnly()) {
     RawKeyBytes rawKey = deriveChildKeyBytesFromPublic(parent, childNumber);
     return new DeterministicKey(
         HDUtils.append(parent.getPath(), childNumber),
         rawKey.chainCode,
         ECKey.CURVE.getCurve().decodePoint(rawKey.keyBytes), // c'tor will compress
         null,
         parent);
   } else {
     RawKeyBytes rawKey = deriveChildKeyBytesFromPrivate(parent, childNumber);
     return new DeterministicKey(
         HDUtils.append(parent.getPath(), childNumber),
         rawKey.chainCode,
         new BigInteger(1, rawKey.keyBytes),
         parent);
   }
 }
Exemplo n.º 5
0
 /**
  * Deserialize an HD Key.
  *
  * @param parent The parent node in the given key's deterministic hierarchy.
  */
 public static DeterministicKey deserialize(
     NetworkParameters params, byte[] serializedKey, @Nullable DeterministicKey parent) {
   ByteBuffer buffer = ByteBuffer.wrap(serializedKey);
   int header = buffer.getInt();
   if (header != params.getBip32HeaderPriv() && header != params.getBip32HeaderPub())
     throw new IllegalArgumentException(
         "Unknown header bytes: " + toBase58(serializedKey).substring(0, 4));
   boolean pub = header == params.getBip32HeaderPub();
   int depth =
       buffer.get() & 0xFF; // convert signed byte to positive int since depth cannot be negative
   final int parentFingerprint = buffer.getInt();
   final int i = buffer.getInt();
   final ChildNumber childNumber = new ChildNumber(i);
   ImmutableList<ChildNumber> path;
   if (parent != null) {
     if (parentFingerprint == 0)
       throw new IllegalArgumentException("Parent was provided but this key doesn't have one");
     if (parent.getFingerprint() != parentFingerprint)
       throw new IllegalArgumentException("Parent fingerprints don't match");
     path = HDUtils.append(parent.getPath(), childNumber);
     if (path.size() != depth) throw new IllegalArgumentException("Depth does not match");
   } else {
     if (depth >= 1)
       // We have been given a key that is not a root key, yet we lack the object representing the
       // parent.
       // This can happen when deserializing an account key for a watching wallet.  In this case,
       // we assume that
       // the client wants to conceal the key's position in the hierarchy.  The path is truncated
       // at the
       // parent's node.
       path = ImmutableList.of(childNumber);
     else path = ImmutableList.of();
   }
   byte[] chainCode = new byte[32];
   buffer.get(chainCode);
   byte[] data = new byte[33];
   buffer.get(data);
   checkArgument(!buffer.hasRemaining(), "Found unexpected data in key");
   if (pub) {
     return new DeterministicKey(
         path,
         chainCode,
         new LazyECPoint(ECKey.CURVE.getCurve(), data),
         parent,
         depth,
         parentFingerprint);
   } else {
     return new DeterministicKey(
         path, chainCode, new BigInteger(1, data), parent, depth, parentFingerprint);
   }
 }
Exemplo n.º 6
0
 /**
  * Generates a new deterministic key from the given seed, which can be any arbitrary byte array.
  * However resist the temptation to use a string as the seed - any key derived from a password is
  * likely to be weak and easily broken by attackers (this is not theoretical, people have had
  * money stolen that way). This method checks that the given seed is at least 64 bits long.
  *
  * @throws HDDerivationException if generated master key is invalid (private key 0 or >= n).
  * @throws IllegalArgumentException if the seed is less than 8 bytes and could be brute forced.
  */
 public static DeterministicKey createMasterPrivateKey(byte[] seed) throws HDDerivationException {
   checkArgument(seed.length > 8, "Seed is too short and could be brute forced");
   // Calculate I = HMAC-SHA512(key="Bitcoin seed", msg=S)
   byte[] i = HDUtils.hmacSha512(MASTER_HMAC_SHA512, seed);
   // Split I into two 32-byte sequences, Il and Ir.
   // Use Il as master secret key, and Ir as master chain code.
   checkState(i.length == 64, i.length);
   byte[] il = Arrays.copyOfRange(i, 0, 32);
   byte[] ir = Arrays.copyOfRange(i, 32, 64);
   Arrays.fill(i, (byte) 0);
   DeterministicKey masterPrivKey = createMasterPrivKeyFromBytes(il, ir);
   Arrays.fill(il, (byte) 0);
   Arrays.fill(ir, (byte) 0);
   return masterPrivKey;
 }
Exemplo n.º 7
0
 private static RawKeyBytes deriveChildKeyBytesFromPublic(
     DeterministicKey parent, ChildNumber childNumber) throws HDDerivationException {
   checkArgument(!childNumber.isHardened(), "Can't use private derivation with public keys only.");
   byte[] parentPublicKey = ECKey.compressPoint(parent.getPubKeyPoint()).getEncoded();
   assert parentPublicKey.length == 33 : parentPublicKey.length;
   ByteBuffer data = ByteBuffer.allocate(37);
   data.put(parentPublicKey);
   data.putInt(childNumber.i());
   byte[] i = HDUtils.hmacSha512(parent.getChainCode(), data.array());
   assert i.length == 64 : i.length;
   byte[] il = Arrays.copyOfRange(i, 0, 32);
   byte[] chainCode = Arrays.copyOfRange(i, 32, 64);
   BigInteger ilInt = new BigInteger(1, il);
   assertLessThanN(ilInt, "Illegal derived key: I_L >= n");
   ECPoint Ki = ECKey.CURVE.getG().multiply(ilInt).add(parent.getPubKeyPoint());
   assertNonInfinity(Ki, "Illegal derived key: derived public key equals infinity.");
   return new RawKeyBytes(Ki.getEncoded(true), chainCode);
 }
Exemplo n.º 8
0
 private static RawKeyBytes deriveChildKeyBytesFromPrivate(
     DeterministicKey parent, ChildNumber childNumber) throws HDDerivationException {
   checkArgument(parent.hasPrivKey(), "Parent key must have private key bytes for this method.");
   byte[] parentPublicKey = ECKey.compressPoint(parent.getPubKeyPoint()).getEncoded();
   assert parentPublicKey.length == 33 : parentPublicKey.length;
   ByteBuffer data = ByteBuffer.allocate(37);
   if (childNumber.isHardened()) {
     data.put(parent.getPrivKeyBytes33());
   } else {
     data.put(parentPublicKey);
   }
   data.putInt(childNumber.i());
   byte[] i = HDUtils.hmacSha512(parent.getChainCode(), data.array());
   assert i.length == 64 : i.length;
   byte[] il = Arrays.copyOfRange(i, 0, 32);
   byte[] chainCode = Arrays.copyOfRange(i, 32, 64);
   BigInteger ilInt = new BigInteger(1, il);
   assertLessThanN(ilInt, "Illegal derived key: I_L >= n");
   final BigInteger priv = parent.getPrivKey();
   BigInteger ki = priv.add(ilInt).mod(ECKey.CURVE.getN());
   assertNonZero(ki, "Illegal derived key: derived private key equals 0.");
   return new RawKeyBytes(ki.toByteArray(), chainCode);
 }
Exemplo n.º 9
0
  private static RawKeyBytes deriveChildKeyBytes(DeterministicKey parent, ChildNumber childNumber)
      throws HDDerivationException {

    byte[] parentPublicKey = HDUtils.getBytes(parent.getPubPoint());
    assert parentPublicKey.length == 33 : parentPublicKey.length;
    ByteBuffer data = ByteBuffer.allocate(37);
    if (childNumber.isPrivateDerivation()) {
      data.put(parent.getPrivKeyBytes33());
    } else {
      data.put(parentPublicKey);
    }
    data.putInt(childNumber.getI());
    byte[] i = HDUtils.hmacSha512(parent.getChainCode(), data.array());
    assert i.length == 64 : i.length;
    byte[] il = Arrays.copyOfRange(i, 0, 32);
    byte[] chainCode = Arrays.copyOfRange(i, 32, 64);
    BigInteger ilInt = HDUtils.toBigInteger(il);
    assertLessThanN(ilInt, "Illegal derived key: I_L >= n");
    byte[] keyBytes;
    final BigInteger privAsFieldElement = parent.getPrivAsFieldElement();
    if (privAsFieldElement != null) {
      BigInteger ki = privAsFieldElement.add(ilInt).mod(HDUtils.getEcParams().getN());
      assertNonZero(ki, "Illegal derived key: derived private key equals 0.");
      keyBytes = ki.toByteArray();
    } else {
      checkArgument(
          !childNumber.isPrivateDerivation(),
          "Can't use private derivation with public keys only.");
      ECPoint Ki = HDUtils.getEcParams().getG().multiply(ilInt).add(parent.getPubPoint());
      checkArgument(
          !Ki.equals(HDUtils.getCurve().getInfinity()),
          "Illegal derived key: derived public key equals infinity.");
      keyBytes = HDUtils.toCompressed(Ki.getEncoded());
    }
    return new RawKeyBytes(keyBytes, chainCode);
  }
Exemplo n.º 10
0
/**
 * Implementation of the (public derivation version) deterministic wallet child key generation
 * algorithm.
 */
public final class HDKeyDerivation {

  private HDKeyDerivation() {}

  private static final HMac MASTER_HMAC_SHA512 =
      HDUtils.createHmacSha512Digest("Bitcoin seed".getBytes());

  /**
   * Generates a new deterministic key from the given seed, which can be any arbitrary byte array.
   * However resist the temptation to use a string as the seed - any key derived from a password is
   * likely to be weak and easily broken by attackers (this is not theoretical, people have had
   * money stolen that way). This method checks that the given seed is at least 64 bits long.
   *
   * @throws HDDerivationException if generated master key is invalid (private key 0 or >= n).
   * @throws IllegalArgumentException if the seed is less than 8 bytes and could be brute forced.
   */
  public static DeterministicKey createMasterPrivateKey(byte[] seed) throws HDDerivationException {
    checkArgument(seed.length > 8, "Seed is too short and could be brute forced");
    // Calculate I = HMAC-SHA512(key="Bitcoin seed", msg=S)
    byte[] i = HDUtils.hmacSha512(MASTER_HMAC_SHA512, seed);
    // Split I into two 32-byte sequences, Il and Ir.
    // Use Il as master secret key, and Ir as master chain code.
    checkState(i.length == 64, i.length);
    byte[] il = Arrays.copyOfRange(i, 0, 32);
    byte[] ir = Arrays.copyOfRange(i, 32, 64);
    Arrays.fill(i, (byte) 0);
    DeterministicKey masterPrivKey = createMasterPrivKeyFromBytes(il, ir);
    Arrays.fill(il, (byte) 0);
    Arrays.fill(ir, (byte) 0);
    return masterPrivKey;
  }

  /** @throws HDDerivationException if privKeyBytes is invalid (0 or >= n). */
  public static DeterministicKey createMasterPrivKeyFromBytes(byte[] privKeyBytes, byte[] chainCode)
      throws HDDerivationException {
    BigInteger privateKeyFieldElt = HDUtils.toBigInteger(privKeyBytes);
    assertNonZero(privateKeyFieldElt, "Generated master key is invalid.");
    assertLessThanN(privateKeyFieldElt, "Generated master key is invalid.");
    return new DeterministicKey(
        ImmutableList.<ChildNumber>of(), chainCode, null, privateKeyFieldElt, null);
  }

  public static DeterministicKey createMasterPubKeyFromBytes(byte[] pubKeyBytes, byte[] chainCode) {
    return new DeterministicKey(
        ImmutableList.<ChildNumber>of(),
        chainCode,
        HDUtils.getCurve().decodePoint(pubKeyBytes),
        null,
        null);
  }

  /**
   * @param childNumber the "extended" child number, ie. with the 0x80000000 bit specifying
   *     private/public derivation.
   */
  public static DeterministicKey deriveChildKey(DeterministicKey parent, int childNumber) {
    return deriveChildKey(parent, new ChildNumber(childNumber));
  }

  /**
   * @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).
   */
  public static DeterministicKey deriveChildKey(DeterministicKey parent, ChildNumber childNumber)
      throws HDDerivationException {

    RawKeyBytes rawKey = deriveChildKeyBytes(parent, childNumber);
    return new DeterministicKey(
        HDUtils.append(parent.getChildNumberPath(), childNumber),
        rawKey.chainCode,
        parent.hasPrivate() ? null : HDUtils.getCurve().decodePoint(rawKey.keyBytes),
        parent.hasPrivate() ? HDUtils.toBigInteger(rawKey.keyBytes) : null,
        parent);
  }

  private static RawKeyBytes deriveChildKeyBytes(DeterministicKey parent, ChildNumber childNumber)
      throws HDDerivationException {

    byte[] parentPublicKey = HDUtils.getBytes(parent.getPubPoint());
    assert parentPublicKey.length == 33 : parentPublicKey.length;
    ByteBuffer data = ByteBuffer.allocate(37);
    if (childNumber.isPrivateDerivation()) {
      data.put(parent.getPrivKeyBytes33());
    } else {
      data.put(parentPublicKey);
    }
    data.putInt(childNumber.getI());
    byte[] i = HDUtils.hmacSha512(parent.getChainCode(), data.array());
    assert i.length == 64 : i.length;
    byte[] il = Arrays.copyOfRange(i, 0, 32);
    byte[] chainCode = Arrays.copyOfRange(i, 32, 64);
    BigInteger ilInt = HDUtils.toBigInteger(il);
    assertLessThanN(ilInt, "Illegal derived key: I_L >= n");
    byte[] keyBytes;
    final BigInteger privAsFieldElement = parent.getPrivAsFieldElement();
    if (privAsFieldElement != null) {
      BigInteger ki = privAsFieldElement.add(ilInt).mod(HDUtils.getEcParams().getN());
      assertNonZero(ki, "Illegal derived key: derived private key equals 0.");
      keyBytes = ki.toByteArray();
    } else {
      checkArgument(
          !childNumber.isPrivateDerivation(),
          "Can't use private derivation with public keys only.");
      ECPoint Ki = HDUtils.getEcParams().getG().multiply(ilInt).add(parent.getPubPoint());
      checkArgument(
          !Ki.equals(HDUtils.getCurve().getInfinity()),
          "Illegal derived key: derived public key equals infinity.");
      keyBytes = HDUtils.toCompressed(Ki.getEncoded());
    }
    return new RawKeyBytes(keyBytes, chainCode);
  }

  private static void assertNonZero(BigInteger integer, String errorMessage) {
    checkArgument(!integer.equals(BigInteger.ZERO), errorMessage);
  }

  private static void assertLessThanN(BigInteger integer, String errorMessage) {
    checkArgument(integer.compareTo(HDUtils.getEcParams().getN()) < 0, errorMessage);
  }

  private static class RawKeyBytes {
    private final byte[] keyBytes, chainCode;

    private RawKeyBytes(byte[] keyBytes, byte[] chainCode) {
      this.keyBytes = keyBytes;
      this.chainCode = chainCode;
    }
  }
}
Exemplo n.º 11
0
 private static void assertLessThanN(BigInteger integer, String errorMessage) {
   checkArgument(integer.compareTo(HDUtils.getEcParams().getN()) < 0, errorMessage);
 }
Exemplo n.º 12
0
 /**
  * Returns the path of this key as a human readable string starting with M to indicate the master
  * key.
  */
 public String getPathAsString() {
   return HDUtils.formatPath(getPath());
 }
Exemplo n.º 13
0
/**
 * Implementation of the <a
 * href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki">BIP 32</a> deterministic
 * wallet child key generation algorithm.
 */
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());

  /**
   * Generates a new deterministic key from the given seed, which can be any arbitrary byte array.
   * However resist the temptation to use a string as the seed - any key derived from a password is
   * likely to be weak and easily broken by attackers (this is not theoretical, people have had
   * money stolen that way). This method checks that the given seed is at least 64 bits long.
   *
   * @throws HDDerivationException if generated master key is invalid (private key 0 or >= n).
   * @throws IllegalArgumentException if the seed is less than 8 bytes and could be brute forced.
   */
  public static DeterministicKey createMasterPrivateKey(byte[] seed) throws HDDerivationException {
    checkArgument(seed.length > 8, "Seed is too short and could be brute forced");
    // Calculate I = HMAC-SHA512(key="Bitcoin seed", msg=S)
    byte[] i = HDUtils.hmacSha512(MASTER_HMAC_SHA512, seed);
    // Split I into two 32-byte sequences, Il and Ir.
    // Use Il as master secret key, and Ir as master chain code.
    checkState(i.length == 64, i.length);
    byte[] il = Arrays.copyOfRange(i, 0, 32);
    byte[] ir = Arrays.copyOfRange(i, 32, 64);
    Arrays.fill(i, (byte) 0);
    DeterministicKey masterPrivKey = createMasterPrivKeyFromBytes(il, ir);
    Arrays.fill(il, (byte) 0);
    Arrays.fill(ir, (byte) 0);
    // Child deterministic keys will chain up to their parents to find the keys.
    masterPrivKey.setCreationTimeSeconds(Utils.currentTimeSeconds());
    return masterPrivKey;
  }

  /** @throws HDDerivationException if privKeyBytes is invalid (0 or >= n). */
  public static DeterministicKey createMasterPrivKeyFromBytes(byte[] privKeyBytes, byte[] chainCode)
      throws HDDerivationException {
    BigInteger priv = new BigInteger(1, privKeyBytes);
    assertNonZero(priv, "Generated master key is invalid.");
    assertLessThanN(priv, "Generated master key is invalid.");
    return new DeterministicKey(ImmutableList.<ChildNumber>of(), chainCode, priv, null);
  }

  public static DeterministicKey createMasterPubKeyFromBytes(byte[] pubKeyBytes, byte[] chainCode) {
    return new DeterministicKey(
        ImmutableList.<ChildNumber>of(),
        chainCode,
        ECKey.CURVE.getCurve().decodePoint(pubKeyBytes),
        null,
        null);
  }

  /**
   * Derives a key given the "extended" child number, ie. with the 0x80000000 bit specifying whether
   * to use hardened derivation or not.
   */
  public static DeterministicKey deriveChildKey(DeterministicKey parent, int childNumber) {
    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).
   */
  public static DeterministicKey deriveChildKey(DeterministicKey parent, ChildNumber childNumber)
      throws HDDerivationException {
    if (parent.isPubKeyOnly()) {
      RawKeyBytes rawKey = deriveChildKeyBytesFromPublic(parent, childNumber);
      return new DeterministicKey(
          HDUtils.append(parent.getPath(), childNumber),
          rawKey.chainCode,
          ECKey.CURVE.getCurve().decodePoint(rawKey.keyBytes), // c'tor will compress
          null,
          parent);
    } else {
      RawKeyBytes rawKey = deriveChildKeyBytesFromPrivate(parent, childNumber);
      return new DeterministicKey(
          HDUtils.append(parent.getPath(), childNumber),
          rawKey.chainCode,
          new BigInteger(1, rawKey.keyBytes),
          parent);
    }
  }

  private static RawKeyBytes deriveChildKeyBytesFromPrivate(
      DeterministicKey parent, ChildNumber childNumber) throws HDDerivationException {
    checkArgument(parent.hasPrivKey(), "Parent key must have private key bytes for this method.");
    byte[] parentPublicKey = ECKey.compressPoint(parent.getPubKeyPoint()).getEncoded();
    assert parentPublicKey.length == 33 : parentPublicKey.length;
    ByteBuffer data = ByteBuffer.allocate(37);
    if (childNumber.isHardened()) {
      data.put(parent.getPrivKeyBytes33());
    } else {
      data.put(parentPublicKey);
    }
    data.putInt(childNumber.i());
    byte[] i = HDUtils.hmacSha512(parent.getChainCode(), data.array());
    assert i.length == 64 : i.length;
    byte[] il = Arrays.copyOfRange(i, 0, 32);
    byte[] chainCode = Arrays.copyOfRange(i, 32, 64);
    BigInteger ilInt = new BigInteger(1, il);
    assertLessThanN(ilInt, "Illegal derived key: I_L >= n");
    final BigInteger priv = parent.getPrivKey();
    BigInteger ki = priv.add(ilInt).mod(ECKey.CURVE.getN());
    assertNonZero(ki, "Illegal derived key: derived private key equals 0.");
    return new RawKeyBytes(ki.toByteArray(), chainCode);
  }

  private static RawKeyBytes deriveChildKeyBytesFromPublic(
      DeterministicKey parent, ChildNumber childNumber) throws HDDerivationException {
    checkArgument(!childNumber.isHardened(), "Can't use private derivation with public keys only.");
    byte[] parentPublicKey = ECKey.compressPoint(parent.getPubKeyPoint()).getEncoded();
    assert parentPublicKey.length == 33 : parentPublicKey.length;
    ByteBuffer data = ByteBuffer.allocate(37);
    data.put(parentPublicKey);
    data.putInt(childNumber.i());
    byte[] i = HDUtils.hmacSha512(parent.getChainCode(), data.array());
    assert i.length == 64 : i.length;
    byte[] il = Arrays.copyOfRange(i, 0, 32);
    byte[] chainCode = Arrays.copyOfRange(i, 32, 64);
    BigInteger ilInt = new BigInteger(1, il);
    assertLessThanN(ilInt, "Illegal derived key: I_L >= n");
    ECPoint Ki = ECKey.CURVE.getG().multiply(ilInt).add(parent.getPubKeyPoint());
    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) {
    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) {
    if (integer.compareTo(ECKey.CURVE.getN()) > 0) throw new HDDerivationException(errorMessage);
  }

  private static class RawKeyBytes {
    private final byte[] keyBytes, chainCode;

    private RawKeyBytes(byte[] keyBytes, byte[] chainCode) {
      this.keyBytes = keyBytes;
      this.chainCode = chainCode;
    }
  }
}
Exemplo n.º 14
0
 /**
  * Returns all the key chains found in the given list of keys. Typically there will only be one,
  * but in the case of key rotation it can happen that there are multiple chains found.
  */
 public static List<DeterministicKeyChain> fromProtobuf(
     List<Protos.Key> keys, @Nullable KeyCrypter crypter) throws UnreadableWalletException {
   List<DeterministicKeyChain> chains = newLinkedList();
   DeterministicSeed seed = null;
   DeterministicKeyChain chain = null;
   int lookaheadSize = -1;
   for (Protos.Key key : keys) {
     final Protos.Key.Type t = key.getType();
     if (t == Protos.Key.Type.DETERMINISTIC_MNEMONIC) {
       if (chain != null) {
         checkState(lookaheadSize >= 0);
         chain.setLookaheadSize(lookaheadSize);
         chain.maybeLookAhead();
         chains.add(chain);
         chain = null;
       }
       long timestamp = key.getCreationTimestamp() / 1000;
       String passphrase = DEFAULT_PASSPHRASE_FOR_MNEMONIC; // FIXME allow non-empty passphrase
       if (key.hasSecretBytes()) {
         seed = new DeterministicSeed(key.getSecretBytes().toStringUtf8(), passphrase, timestamp);
       } else if (key.hasEncryptedData()) {
         EncryptedData data =
             new EncryptedData(
                 key.getEncryptedData().getInitialisationVector().toByteArray(),
                 key.getEncryptedData().getEncryptedPrivateKey().toByteArray());
         seed = new DeterministicSeed(data, timestamp);
       } else {
         throw new UnreadableWalletException("Malformed key proto: " + key.toString());
       }
       if (log.isDebugEnabled()) log.debug("Deserializing: DETERMINISTIC_MNEMONIC: {}", seed);
     } else if (t == Protos.Key.Type.DETERMINISTIC_KEY) {
       if (!key.hasDeterministicKey())
         throw new UnreadableWalletException(
             "Deterministic key missing extra data: " + key.toString());
       byte[] chainCode = key.getDeterministicKey().getChainCode().toByteArray();
       // Deserialize the path through the tree.
       LinkedList<ChildNumber> path = newLinkedList();
       for (int i : key.getDeterministicKey().getPathList()) path.add(new ChildNumber(i));
       // Deserialize the public key and path.
       ECPoint pubkey = ECKey.CURVE.getCurve().decodePoint(key.getPublicKey().toByteArray());
       final ImmutableList<ChildNumber> immutablePath = ImmutableList.copyOf(path);
       // Possibly create the chain, if we didn't already do so yet.
       boolean isWatchingAccountKey = false;
       boolean isFollowingKey = false;
       // save previous chain if any if the key is marked as following. Current key and the next
       // ones are to be
       // placed in new following key chain
       if (key.getDeterministicKey().getIsFollowing()) {
         if (chain != null) {
           checkState(lookaheadSize >= 0);
           chain.setLookaheadSize(lookaheadSize);
           chain.maybeLookAhead();
           chains.add(chain);
           chain = null;
           seed = null;
         }
         isFollowingKey = true;
       }
       if (chain == null) {
         if (seed == null) {
           DeterministicKey accountKey =
               new DeterministicKey(immutablePath, chainCode, pubkey, null, null);
           if (!accountKey.getPath().equals(ACCOUNT_ZERO_PATH))
             throw new UnreadableWalletException(
                 "Expecting account key but found key with path: "
                     + HDUtils.formatPath(accountKey.getPath()));
           chain = new DeterministicKeyChain(accountKey, isFollowingKey);
           isWatchingAccountKey = true;
         } else {
           chain = new DeterministicKeyChain(seed, crypter);
           chain.lookaheadSize = LAZY_CALCULATE_LOOKAHEAD;
           // If the seed is encrypted, then the chain is incomplete at this point. However, we
           // will load
           // it up below as we parse in the keys. We just need to check at the end that we've
           // loaded
           // everything afterwards.
         }
       }
       // Find the parent key assuming this is not the root key, and not an account key for a
       // watching chain.
       DeterministicKey parent = null;
       if (!path.isEmpty() && !isWatchingAccountKey) {
         ChildNumber index = path.removeLast();
         parent = chain.hierarchy.get(path, false, false);
         path.add(index);
       }
       DeterministicKey detkey;
       if (key.hasSecretBytes()) {
         // Not encrypted: private key is available.
         final BigInteger priv = new BigInteger(1, key.getSecretBytes().toByteArray());
         detkey = new DeterministicKey(immutablePath, chainCode, pubkey, priv, parent);
       } else {
         if (key.hasEncryptedData()) {
           Protos.EncryptedData proto = key.getEncryptedData();
           EncryptedData data =
               new EncryptedData(
                   proto.getInitialisationVector().toByteArray(),
                   proto.getEncryptedPrivateKey().toByteArray());
           checkNotNull(crypter, "Encountered an encrypted key but no key crypter provided");
           detkey = new DeterministicKey(immutablePath, chainCode, crypter, pubkey, data, parent);
         } else {
           // No secret key bytes and key is not encrypted: either a watching key or private key
           // bytes
           // will be rederived on the fly from the parent.
           detkey = new DeterministicKey(immutablePath, chainCode, pubkey, null, parent);
         }
       }
       if (key.hasCreationTimestamp())
         detkey.setCreationTimeSeconds(key.getCreationTimestamp() / 1000);
       if (log.isDebugEnabled()) log.debug("Deserializing: DETERMINISTIC_KEY: {}", detkey);
       if (!isWatchingAccountKey) {
         // If the non-encrypted case, the non-leaf keys (account, internal, external) have already
         // been
         // rederived and inserted at this point and the two lines below are just a no-op. In the
         // encrypted
         // case though, we can't rederive and we must reinsert, potentially building the heirarchy
         // object
         // if need be.
         if (path.size() == 0) {
           // Master key.
           chain.rootKey = detkey;
           chain.hierarchy = new DeterministicHierarchy(detkey);
         } else if (path.size() == 2) {
           if (detkey.getChildNumber().num() == 0) {
             chain.externalKey = detkey;
             chain.issuedExternalKeys = key.getDeterministicKey().getIssuedSubkeys();
             lookaheadSize = Math.max(lookaheadSize, key.getDeterministicKey().getLookaheadSize());
           } else if (detkey.getChildNumber().num() == 1) {
             chain.internalKey = detkey;
             chain.issuedInternalKeys = key.getDeterministicKey().getIssuedSubkeys();
           }
         }
       }
       chain.hierarchy.putKey(detkey);
       chain.basicKeyChain.importKey(detkey);
     }
   }
   if (chain != null) {
     checkState(lookaheadSize >= 0);
     chain.setLookaheadSize(lookaheadSize);
     chain.maybeLookAhead();
     chains.add(chain);
   }
   return chains;
 }
Exemplo n.º 15
0
 /** Returns freshly derived key/s that have not been returned by this method before. */
 @Override
 public List<DeterministicKey> getKeys(KeyPurpose purpose, int numberOfKeys) {
   checkArgument(numberOfKeys > 0);
   lock.lock();
   try {
     DeterministicKey parentKey;
     int index;
     switch (purpose) {
         // Map both REFUND and RECEIVE_KEYS to the same branch for now. Refunds are a feature of
         // the BIP 70
         // payment protocol. Later we may wish to map it to a different branch (in a new wallet
         // version?).
         // This would allow a watching wallet to only be able to see inbound payments, but not
         // change
         // (i.e. spends) or refunds. Might be useful for auditing ...
       case RECEIVE_FUNDS:
       case REFUND:
         issuedExternalKeys += numberOfKeys;
         index = issuedExternalKeys;
         parentKey = externalKey;
         break;
       case AUTHENTICATION:
       case CHANGE:
         issuedInternalKeys += numberOfKeys;
         index = issuedInternalKeys;
         parentKey = internalKey;
         break;
       default:
         throw new UnsupportedOperationException();
     }
     // Optimization: potentially do a very quick key generation for just the number of keys we
     // need if we
     // didn't already create them, ignoring the configured lookahead size. This ensures we'll be
     // able to
     // retrieve the keys in the following loop, but if we're totally fresh and didn't get a chance
     // to
     // calculate the lookahead keys yet, this will not block waiting to calculate 100+ EC point
     // multiplies.
     // On slow/crappy Android phones looking ahead 100 keys can take ~5 seconds but the OS will
     // kill us
     // if we block for just one second on the UI thread. Because UI threads may need an address in
     // order
     // to render the screen, we need getKeys to be fast even if the wallet is totally brand new
     // and lookahead
     // didn't happen yet.
     //
     // It's safe to do this because when a network thread tries to calculate a Bloom filter, we'll
     // go ahead
     // and calculate the full lookahead zone there, so network requests will always use the right
     // amount.
     List<DeterministicKey> lookahead = maybeLookAhead(parentKey, index, 0, 0);
     basicKeyChain.importKeys(lookahead);
     List<DeterministicKey> keys = new ArrayList<DeterministicKey>(numberOfKeys);
     for (int i = 0; i < numberOfKeys; i++) {
       ImmutableList<ChildNumber> path =
           HDUtils.append(parentKey.getPath(), new ChildNumber(index - numberOfKeys + i, false));
       DeterministicKey k = hierarchy.get(path, false, false);
       // Just a last minute sanity check before we hand the key out to the app for usage. This
       // isn't inspired
       // by any real problem reports from bitcoinj users, but I've heard of cases via the
       // grapevine of
       // places that lost money due to bitflips causing addresses to not match keys. Of course in
       // an
       // environment with flaky RAM there's no real way to always win: bitflips could be
       // introduced at any
       // other layer. But as we're potentially retrieving from long term storage here, check
       // anyway.
       checkForBitFlip(k);
       keys.add(k);
     }
     return keys;
   } finally {
     lock.unlock();
   }
 }