public IEnumerable <IReadonlyBytesKeyValuePair> Seek(IReadOnlyList <byte> key) { // TODO snapshot is not support, use ulong.MaxValue (smallest) instead var interkey = new InternalKey(key.ToArray(), InternalKey.MaxSequenceNumber, InternalKey.ValueTypeForSeek); return(FilterDeleted(_dataProvider.Seek(interkey))); }
/// <summary> /// Computes a hash for the current key to determine what its child's hash should be, in order to verify a supposed parent matches a supposed child. /// </summary> /// <returns>Returns a hash used as an extended key fingerprint.</returns> private byte[] GetChildFingerprint() { // Obtain our public key (compressed). byte[] publicKeyCompressed = InternalKey.ToPublicKeyArray(true, false); // Compute RIPEMD160(SHA256(compressed public key)). Memory <byte> hash = RIPEMD160.ComputeHash(SHA256.ComputeHash(publicKeyCompressed)); // Obtain our fingerprint from our hash. return(hash.Slice(0, FINGERPRINT_SIZE).ToArray()); }
/// <summary> /// Obtains the public key for this current key instance (if private, derives public, if public, returns as is). /// </summary> /// <returns>Returns this key if it is a public key, otherwise obtains the public key from this key.</returns> public ExtendedKey GetExtendedPublicKey() { // If this is already a public key, return itself if (KeyType == EthereumEcdsaKeyType.Public) { return(this); } // This is a private key, so we derive our public key information // at this level from this private key. return(new ExtendedKey( EthereumEcdsa.Create(InternalKey.ToPublicKeyArray(), EthereumEcdsaKeyType.Public), ChainCode, Depth, ChildIndex, 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> /// Computes the child key and child chain code for a given child key/index relative from this extended key. /// </summary> /// <param name="index">The child key/index to derive a key/chain code for.</param> /// <returns>Returns a key and chain code for a child relative from this extended key. Derives a key of the same type as this key.</returns> private (EthereumEcdsa childKey, byte[] childChainCode) GetChildKeyInternal(uint index) { // Declare a hash we will obtain of our key. byte[] hash = null; // If this is a hardened directory/key/index if (KeyPath.CheckHardenedDirectoryIndex(index)) { // Verify we aren't trying to derive a hardened key from a public key (since private is required). if (KeyType == EthereumEcdsaKeyType.Public) { // Throw an exception because hardened keys mean we need the private key, but this is the public key. throw new ArgumentException("Hierarchically deterministic child key cannot be derived from a public key when the child key index is hardened. Hardened keys can only be derived when the private key is known."); } // Obtain the message to hash. (0x00 byte prefixing the private key to pad it to 33 bytes long). byte[] hashMessage = new byte[] { 0 }.Concat(InternalKey.ToPrivateKeyArray()); // Next we hash our key data. hash = ComputeChildHash(index, hashMessage); } else { // Compute the hash on our public key and index. hash = ComputeChildHash(index, 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); // Convert the key data to an integer BigInteger childKeyInt = BigIntegerConverter.GetBigInteger(childKeyData, false, childChainCode.Length); // If the child key is above N if (childKeyInt >= Secp256k1Curve.N) { throw new ArgumentException("Calculated child key value cannot exceed or equal N on the secp256k1 curve. Hierarchically deterministic child key cannot derive here. Try again."); } // Define our resulting key to obtain EthereumEcdsa childKey = null; // Obtain our child key depending on type.; if (KeyType == EthereumEcdsaKeyType.Public) { // Obtain our public key and add it to G * childKey var q = Secp256k1Curve.Parameters.Curve.DecodePoint(InternalKey.ToPublicKeyArray(true, false)); q = Secp256k1Curve.Parameters.G.Multiply(childKeyInt.ToBouncyCastleBigInteger()).Add(q); if (q.IsInfinity) { throw new ArgumentException("Calculated child key value point is infinity. This is a very rare occurrence. Hierarchically deterministic child key cannot derive here."); } // Normalize our point. q = q.Normalize(); var p = Secp256k1Curve.DomainParameters.Curve.CreatePoint(q.XCoord.ToBigInteger(), q.YCoord.ToBigInteger()); var encoded = p.GetEncoded(compressed: true); // Derive our child data. childKey = EthereumEcdsa.Create(encoded, EthereumEcdsaKeyType.Public); } else { // Add our private key to our parsed new key, mod N, to derive our new key. BigInteger computedChildKeyInt = (BigIntegerConverter.GetBigInteger(InternalKey.ToPrivateKeyArray()) + childKeyInt) % Secp256k1Curve.N; // Verify our computed child key is non-zero if (computedChildKeyInt == 0) { throw new ArgumentException("Calculated child private key is zero. This is a very rare occurrence. Hierarchically deterministic child key cannot derive here."); } // Obtain our new key from this byte[] computedChildKeyData = BigIntegerConverter.GetBytes(computedChildKeyInt, EthereumEcdsa.PRIVATE_KEY_SIZE); // Initialize our key childKey = EthereumEcdsa.Create(computedChildKeyData, EthereumEcdsaKeyType.Private); } // Return our obtained data. return(childKey, childChainCode); }