예제 #1
0
        /// <summary>
        /// Returns mnemonic (seed words)
        /// </summary>
        /// <exception cref="ObjectDisposedException"/>
        /// <returns>mnemonic (seed words)</returns>
        public string ToMnemonic()
        {
            if (entropy == null)
            {
                throw new ObjectDisposedException(nameof(BetterMnemonic));
            }

            var stream  = new FastStream(100);
            int version = 1;
            int depth   = DerivationPath.Indexes.Length;

            Debug.Assert(version <= 0b00001111);
            Debug.Assert(depth <= 0b00001111);
            stream.Write((byte)(version << 4 | depth));
            foreach (var item in DerivationPath.Indexes)
            {
                stream.Write(item);
            }
            stream.WriteWithCompactIntLength(entropy);

            uint[] wordIndexes = Convert8To11(stream);

            StringBuilder sb = new StringBuilder(wordIndexes.Length * 8);

            for (int i = 0; i < wordIndexes.Length; i++)
            {
                sb.Append($"{allWords[wordIndexes[i]]} ");
            }

            // no space at the end.
            sb.Length--;
            return(sb.ToString());
        }
예제 #2
0
        public void WriteWithCompactIntLength(byte[] data, byte[] expBuffer, int expPos)
        {
            var stream = new FastStream(expPos);

            stream.WriteWithCompactIntLength(data);

            Assert.Equal(expBuffer, stream.ToByteArray());
            Helper.ComparePrivateField(stream, "position", expPos);
        }
예제 #3
0
파일: BIP0137.cs 프로젝트: supaFool/Denovo
        /// <summary>
        /// Returns the 256-bit result of double SHA-256 hash of the message with the added constant used in signing operation.
        /// </summary>
        /// <exception cref="ArgumentNullException"/>
        /// <param name="message">
        /// UTF-8 encoded message to sign (will be normalized using full compatibility decomposition form).
        /// <para/>Note that trailing spaces, new line character,... will not be changed here.
        /// Caller has to decide whether to change those
        /// </param>
        /// <returns>256-bit hash</returns>
        public byte[] GetBytesToSign(string message)
        {
            if (message is null)
            {
                throw new ArgumentNullException(nameof(message), "Message can not be null.");
            }

            using Sha256 hash = new Sha256();
            FastStream stream = new FastStream();

            stream.Write((byte)Constants.MsgSignConst.Length);
            stream.Write(Encoding.UTF8.GetBytes(Constants.MsgSignConst));
            byte[] messageBytes = Encoding.UTF8.GetBytes(message.Normalize(NormalizationForm.FormKD));
            stream.WriteWithCompactIntLength(messageBytes);

            byte[] result = hash.ComputeHashTwice(stream.ToByteArray());
            return(result);
        }
예제 #4
0
        /// <summary>
        /// Initializes a new instance of <see cref="BetterMnemonic"/> using the given arguments.
        /// </summary>
        /// <exception cref="ArgumentException"/>
        /// <exception cref="ArgumentNullException"/>
        /// <exception cref="FormatException"/>
        /// <param name="mnemonic">Mnemonic to use</param>
        /// <param name="wl">Word list to use</param>
        /// <param name="passPhrase">Optional passphrase to extend the key</param>
        public BetterMnemonic(string mnemonic, BIP0039.WordLists wl = BIP0039.WordLists.English, string passPhrase = null)
        {
            if (string.IsNullOrWhiteSpace(mnemonic))
            {
                throw new ArgumentNullException(nameof(mnemonic), "Seed can not be null or empty!");
            }
            allWords = BIP0039.GetAllWords(wl);

            string[] words = mnemonic.Normalize(NormalizationForm.FormKD)
                             .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (!words.All(x => allWords.Contains(x)))
            {
                throw new ArgumentException("Seed has invalid words.", nameof(mnemonic));
            }

            uint[] wordIndexes = new uint[words.Length];
            for (int i = 0; i < words.Length; i++)
            {
                wordIndexes[i] = (uint)Array.IndexOf(allWords, words[i]);
            }

            int  itemIndex = 0;
            int  bitIndex  = 0;
            byte verDepth  = Read(wordIndexes, ref itemIndex, ref bitIndex, 8);
            int  version   = verDepth >> 4;
            int  depth     = verDepth & 0b00001111;

            uint[] pathIndexes = new uint[depth];
            for (int i = 0; i < pathIndexes.Length; i++)
            {
                byte a1 = Read(wordIndexes, ref itemIndex, ref bitIndex, 8);
                byte a2 = Read(wordIndexes, ref itemIndex, ref bitIndex, 8);
                byte a3 = Read(wordIndexes, ref itemIndex, ref bitIndex, 8);
                byte a4 = Read(wordIndexes, ref itemIndex, ref bitIndex, 8);
                pathIndexes[i] = (uint)(a1 | a2 << 8 | a3 << 16 | a4 << 24);
            }
            DerivationPath = new BIP0032Path(pathIndexes);

            byte entLen = Read(wordIndexes, ref itemIndex, ref bitIndex, 8);

            entropy = new byte[entLen];
            for (int i = 0; i < entropy.Length; i++)
            {
                entropy[i] = Read(wordIndexes, ref itemIndex, ref bitIndex, 8);
            }

            // Compute and compare checksum:
            var stream = new FastStream(100);

            Debug.Assert(version <= 0b00001111);
            Debug.Assert(depth <= 0b00001111);
            stream.Write((byte)(version << 4 | depth));
            foreach (var item in DerivationPath.Indexes)
            {
                stream.Write(item);
            }
            stream.WriteWithCompactIntLength(entropy);

            using Sha256 sha = new Sha256();
            byte[] hash = sha.ComputeHash(stream.ToByteArray());

            int rem       = wordIndexes.Length * 11 - (itemIndex * 11) - bitIndex;
            int hashIndex = 0;

            if (rem > 8)
            {
                byte cs1 = Read(wordIndexes, ref itemIndex, ref bitIndex, 8);
                if (hash[hashIndex++] != cs1)
                {
                    throw new FormatException("Invalid checksum.");
                }

                rem -= 8;
            }

            byte cs2 = (byte)((wordIndexes[itemIndex] << (bitIndex - 3)) & 0xff);

            if ((hash[hashIndex] & (0xff << (8 - rem))) != cs2)
            {
                throw new FormatException("Invalid checksum.");
            }

            SetBip32(passPhrase);
        }