/// <summary> /// Obtains a child extended key, relative from this key, at the given child key/index. /// </summary> /// <param name="index">The child key/index to obtain the extended key for, relative from this extended key.</param> /// <returns>Returns a extended key for the provided child key/index, relative from this extended key.</returns> public ExtendedKey GetChildKey(uint index) { // Obtain our key and chain code for our child. (EthereumEcdsa childKey, byte[] childChainCode) = GetChildKeyInternal(index); // Initialize a new extended private key using this current key at the given child key. ExtendedKey extendedChildKey = new ExtendedKey(childKey, childChainCode, Depth + 1, index, GetChildFingerprint()); // Return the extended child key. return(extendedChildKey); }
/// <summary> /// Obtains a child extended key, relative from this key, at the given key path. /// </summary> /// <param name="keyPath">The path of the child extended key to derive, relative from this extended key.</param> /// <returns>Returns an extended key for a child relative from this key, at the provided key path.</returns> public ExtendedKey GetChildKey(KeyPath keyPath) { // Declare our current private key. ExtendedKey current = this; // Loop for each index in the key path. foreach (uint index in keyPath.Indices) { current = current.GetChildKey(index); } // Return our private key return(current); }
/// <summary> /// Determines if the provided key is a child key to this current key. /// </summary> /// <param name="childKey">The key to determine is a child key or not.</param> /// <returns>Returns true if the provided key is a child to this key.</returns> public bool IsChild(ExtendedKey childKey) { // Verify depth if (childKey.Depth != Depth + 1) { return(false); } // If the child's fingerprint is null, it is not a child. if (childKey.Fingerprint == null) { return(false); } // Verify the fingerprint equals byte[] expectedChildFingerprint = GetChildFingerprint(); return(expectedChildFingerprint.SequenceEqual(childKey.Fingerprint)); }
/// <summary> /// Obtains the parent private key if provided the parent public key. This key must be a private key. /// </summary> /// <param name="parentPublicKey">The public key of the parent of this extended key.</param> /// <returns>Returns the private key of the parent of this extended key.</returns> public ExtendedKey GetParentPrivateKey(ExtendedKey parentPublicKey) { // Verify this key is a private key if (KeyType != EthereumEcdsaKeyType.Private) { // This key is not a private key. throw new ArgumentNullException("Could not obtain parent private key. Can only obtain the parent private key of a private key. This key is a public key."); } else if (parentPublicKey == null) { // The public key is null. throw new ArgumentNullException("Could not obtain parent private key. Provided parent public key argument is null."); } else if (parentPublicKey.KeyType == EthereumEcdsaKeyType.Private) { // The public key was not a public key. throw new ArgumentException("Could not obtain parent private key. Provided parent public key argument is not a public key."); } else if (Hardened) { throw new ArgumentException("Could not obtain parent private key if this key is a hardened key."); } else if (Depth == 0) { throw new ArgumentException("Could not obtain parent private key for this key because this key is a top level key."); } else if (!parentPublicKey.IsChild(this)) { // The provided parent public key is not a parent. throw new ArgumentException("Could not obtain parent private key for this key because the provided parent public key argument is not a parent to this key."); } // Obtain the hash used to derive this current key, from the parent. byte[] hash = parentPublicKey.ComputeChildHash(ChildIndex, parentPublicKey.InternalKey.ToPublicKeyArray(true, false)); // Set the child key data as the first 32 bytes of "hash" byte[] childKeyData = new byte[EthereumEcdsa.PRIVATE_KEY_SIZE]; Array.Copy(hash, 0, childKeyData, 0, childKeyData.Length); // Initialize the child chain code byte[] childChainCode = new byte[CHAIN_CODE_SIZE]; // We derive the child chain code as the data immediately following key data. Array.Copy(hash, 32, childChainCode, 0, childChainCode.Length); // Verify the chain code is equal if (!ChainCode.SequenceEqual(childChainCode)) { throw new ArgumentException("Derived chain code from the parent at this key's child index that did not match this key's chain code."); } // Convert the key data to an integer BigInteger childKeyInt = BigIntegerConverter.GetBigInteger(childKeyData, false, childChainCode.Length); // Convert this private key to an integer byte[] thisKeyData = InternalKey.ToPrivateKeyArray(); BigInteger thisKeyInt = BigIntegerConverter.GetBigInteger(thisKeyData, false, thisKeyData.Length); // Compute our parent key BigInteger parentPrivateKeyInt = ((thisKeyInt - childKeyInt) + Secp256k1Curve.N) % Secp256k1Curve.N; // Obtain our new key from this byte[] computedParentKeyData = BigIntegerConverter.GetBytes(parentPrivateKeyInt, EthereumEcdsa.PRIVATE_KEY_SIZE); // Obtain the parent private key EthereumEcdsa parentPrivateKey = EthereumEcdsa.Create(computedParentKeyData, EthereumEcdsaKeyType.Private); // Create the parent extended private key return(new ExtendedKey(parentPrivateKey, parentPublicKey.ChainCode, parentPublicKey.Depth, parentPublicKey.ChildIndex, parentPublicKey.Fingerprint)); }
/// <summary> /// Determines if the provided key is a parent key to this current key. /// </summary> /// <param name="parentKey">The key to determine is a parent key or not.</param> /// <returns>Returns true if the provided key is the parent to this key.</returns> public bool IsParent(ExtendedKey parentKey) { // Determine if this is a child of the parent key. return(parentKey.IsChild(this)); }