Ejemplo n.º 1
0
        /// <summary>
        /// Derives a 512-bit entropy from the BIP-32 instance based on the given path.
        /// </summary>
        /// <exception cref="ArgumentException"/>
        /// <exception cref="ArgumentNullException"/>
        /// <exception cref="ObjectDisposedException"/>
        /// <param name="path">Path to use (all indexes must be hardened)</param>
        /// <returns>512-bit entropy</returns>
        public byte[] DeriveEntropy(BIP0032Path path)
        {
            if (isDisposed)
            {
                throw new ObjectDisposedException(nameof(BIP0085));
            }
            if (path is null)
            {
                throw new ArgumentNullException(nameof(path), "Path can not be null.");
            }
            if (path.Indexes.Any(x => x < HardenedIndex))
            {
                throw new ArgumentException("All indexes inside the given path must be hardened.", nameof(path));
            }

            // BIP-32 treats paths differently, meaning path is the "path" of the child extended key not the child key.
            // Since BIP-85 treats paths as the "path" of the child key itself it must be modified here.
            var path2 = new BIP0032Path(path.Indexes.AsSpan(0, path.Indexes.Length - 1).ToArray());

            using PrivateKey key = bip32.GetPrivateKeys(path2, 1, path.Indexes[^ 1])[0];
Ejemplo n.º 2
0
        public PublicKey[] GetPublicKeys(BIP0032Path path, uint count, uint startIndex = 0, uint step = 1)
        {
            if (isDisposed)
            {
                throw new ObjectDisposedException(nameof(BIP0032));
            }
            if (path is null)
            {
                throw new ArgumentNullException(nameof(path), "Path can not be null!");
            }
            if (ExtendedKeyDepth == byte.MaxValue || ExtendedKeyDepth + path.Indexes.Length + 1 > byte.MaxValue)
            {
                throw new ArgumentOutOfRangeException(nameof(ExtendedKeyDepth), "Can not get children since " +
                                                      "depth will be bigger than 1 byte");
            }

            PublicKey[] result = new PublicKey[count];
            if (!(PrvKey is null))
            {
                PrivateKey[] tempPK = GetPrivateKeys(path, count, startIndex, step);
                for (int j = 0; j < tempPK.Length; j++)
                {
                    result[j] = tempPK[j].ToPublicKey();
                    tempPK[j].Dispose();
                }
                return(result);
            }

            // If we are here it means PrivateKey was null
            bool anyHardened = path.Indexes.Any(x => IsHardendedIndex(x));

            if (anyHardened || IsHardendedIndex(startIndex))
            {
                throw new ArgumentException();
            }

            // Two quick fixes:
            if (count == 0)
            {
                return(null);
            }
            if (step < 1)
            {
                step = 1;
            }

            // First start deriving the extended keys for each index
            BigInteger prevPrvInt = PrvKey.ToBigInt();

            byte[]             prevPubBa    = PubKey.ToByteArray(true);
            EllipticCurvePoint prevPubPoint = PubKey.ToPoint();

            byte[] tempCC = ChainCode;
            byte[] tempLeft;
            EllipticCurveCalculator calc = new EllipticCurveCalculator();

            foreach (var index in path.Indexes)
            {
                // There is no hardened indexes thanks to first check
                FastStream stream = new FastStream(33 + 4);
                stream.Write(prevPubBa);
                stream.WriteBigEndian(index);
                HmacAndSplitData(stream.ToByteArray(), tempCC, out tempLeft, out tempCC);

                BigInteger kTemp = tempLeft.ToBigInt(true, true);
                // Note that we throw an exception here if the values were invalid (highly unlikely)
                // because it is the extended keys, we can't skip anything here.
                if (kTemp == 0 || kTemp >= N)
                {
                    throw new ArgumentException();
                }
                EllipticCurvePoint pt      = calc.AddChecked(calc.MultiplyByG(kTemp), prevPubPoint);
                PublicKey          tempPub = new PublicKey(pt);
                prevPubPoint = tempPub.ToPoint();
                prevPubBa    = tempPub.ToByteArray(true);
            }

            // Then derive the actual keys

            int  i          = 0;
            uint childIndex = startIndex;

            while (i < count)
            {
                // There is no hardened indexes thanks to first check
                FastStream stream = new FastStream(33 + 4);
                stream.Write(prevPubBa);
                stream.WriteBigEndian(childIndex);
                HmacAndSplitData(stream.ToByteArray(), tempCC, out tempLeft, out _);

                BigInteger kTemp = tempLeft.ToBigInt(true, true);
                // Note: we don't throw any exceptions here. We simply ignore invalid values (highly unlikely)
                // and move on to the next index. The returned value will always be filled with expected number of items.
                if (kTemp == 0 || kTemp >= N)
                {
                    continue;
                }
                EllipticCurvePoint pt   = calc.AddChecked(calc.MultiplyByG(kTemp), prevPubPoint);
                PublicKey          temp = new PublicKey(pt);
                result[i++] = temp;

                childIndex += step;
            }
            return(result);
        }
Ejemplo n.º 3
0
        public PrivateKey[] GetPrivateKeys(BIP0032Path path, uint count, uint startIndex = 0, uint step = 1)
        {
            if (isDisposed)
            {
                throw new ObjectDisposedException(nameof(BIP0032));
            }
            if (path is null)
            {
                throw new ArgumentNullException(nameof(path), "Path can not be null!");
            }
            if (PrvKey is null)
            {
                throw new ArgumentNullException(nameof(PrvKey), "Can not get child private keys from extended public key.");
            }
            if (ExtendedKeyDepth == byte.MaxValue || ExtendedKeyDepth + path.Indexes.Length + 1 > byte.MaxValue)
            {
                throw new ArgumentOutOfRangeException(nameof(ExtendedKeyDepth), "Can not get children since " +
                                                      "depth will be bigger than 1 byte");
            }

            // Two quick fixes:
            if (count == 0)
            {
                return(null);
            }
            if (step < 1)
            {
                step = 1;
            }

            // First start deriving the extended keys for each index
            byte[]     prevPrvBa  = PrvKey.ToBytes();
            BigInteger prevPrvInt = PrvKey.ToBigInt();

            byte[] prevPubBa = PubKey.ToByteArray(true);
            byte[] tempCC    = ChainCode;
            byte[] tempLeft;
            foreach (var index in path.Indexes)
            {
                FastStream stream = new FastStream(33 + 4);
                if (IsHardendedIndex(index))
                {
                    stream.Write((byte)0);
                    stream.Write(prevPrvBa);
                }
                else
                {
                    stream.Write(prevPubBa);
                }
                stream.WriteBigEndian(index);
                HmacAndSplitData(stream.ToByteArray(), tempCC, out tempLeft, out tempCC);

                BigInteger kTemp = tempLeft.ToBigInt(true, true);
                // Note that we throw an exception here if the values were invalid (highly unlikely)
                // because it is the extended keys, we can't skip anything here.
                if (kTemp == 0 || kTemp >= N)
                {
                    throw new ArgumentException();
                }
                // Let PrivateKey do the additional checks and throw here
                PrivateKey temp = new PrivateKey((kTemp + prevPrvInt) % N);
                prevPrvInt = temp.ToBigInt();
                prevPrvBa  = temp.ToBytes();
                prevPubBa  = temp.ToPublicKey().ToByteArray(true);
            }

            // Then derive the actual keys
            PrivateKey[] result     = new PrivateKey[count];
            int          i          = 0;
            uint         childIndex = startIndex;

            while (i < count)
            {
                try
                {
                    FastStream stream = new FastStream(33 + 4);
                    if (IsHardendedIndex(childIndex))
                    {
                        stream.Write((byte)0);
                        stream.Write(prevPrvBa);
                    }
                    else
                    {
                        stream.Write(prevPubBa);
                    }
                    stream.WriteBigEndian(childIndex);
                    HmacAndSplitData(stream.ToByteArray(), tempCC, out tempLeft, out _);

                    BigInteger kTemp = tempLeft.ToBigInt(true, true);
                    // Note: we don't throw any exceptions here. We simply ignore invalid values (highly unlikely)
                    // and move on to the next index. The returned value will always be filled with expected number of items.
                    if (kTemp == 0 || kTemp >= N)
                    {
                        continue;
                    }
                    PrivateKey temp = new PrivateKey((kTemp + prevPrvInt) % N);
                    result[i++] = temp;
                }
                catch (ArgumentOutOfRangeException)
                {
                    // Only ignore this type that is thrown by the PrivateKey constructor.
                }
                finally
                {
                    childIndex += step;
                }
            }
            return(result);
        }