/// <inheritdoc />
        public override long Seek(long offset, SeekOrigin origin)
        {
            long pos;

            switch (origin)
            {
            case SeekOrigin.Begin:
                pos = offset;
                break;

            case SeekOrigin.Current:
                pos = Position + offset;
                break;

            case SeekOrigin.End:
                pos = Length + offset;
                break;

            default:
                throw Exceptions.ArgumentOutOfRange("origin", "Unknown seek type.");
            }

            if (pos < 0)
            {
                throw Exceptions.Argument("offset", "Can't seek before the stream start.");
            }
            Position = pos;
            return(pos);
        }
Beispiel #2
0
        /// <inheritdoc />
        public override string GenerateSalt(CrypterOptions options)
        {
            Check.Null("options", options);

            switch (options.GetValue(CrypterOption.Variant, LdapCrypterVariant.SSha))
            {
            case LdapCrypterVariant.Crypt:
                Crypter crypter = options.GetValue <Crypter>(LdapCrypterOption.Crypter);
                if (crypter == null)
                {
                    throw Exceptions.Argument("LdapCrypterOption.Crypter",
                                              "Crypter not set. Did you intend Crypter.TraditionalDes (the slappasswd default)?");
                }

                CrypterOptions crypterOptions = options.GetValue(LdapCrypterOption.CrypterOptions, CrypterOptions.None);
                return("{CRYPT}" + crypter.GenerateSalt(crypterOptions));

            case LdapCrypterVariant.SSha: return("{SSHA}" + Convert.ToBase64String(Security.GenerateRandomBytes(8)));

            case LdapCrypterVariant.SMD5: return("{SMD5}" + Convert.ToBase64String(Security.GenerateRandomBytes(8)));

            case LdapCrypterVariant.Sha: return("{SHA}");

            case LdapCrypterVariant.MD5: return("{MD5}");

            case LdapCrypterVariant.Cleartext: return("{CLEARTEXT}");

            default: throw Exceptions.ArgumentOutOfRange("CrypterOption.Variant", "Unknown variant.");
            }
        }
Beispiel #3
0
        /// <inheritdoc />
        public override string Crypt(byte[] password, string salt)
        {
            Check.Null("password", password);
            Check.Null("salt", salt);

            Match match = _regex.Match(salt);

            if (!match.Success)
            {
                throw Exceptions.Argument("salt", "Invalid salt.");
            }

            byte[] roundsBytes = null, saltBytes = null, crypt = null, input = null;
            try
            {
                string roundsString = match.Groups["rounds"].Value;
                roundsBytes = Base64Encoding.UnixMD5.GetBytes(roundsString);
                int roundsValue = (int)BitPacking.UInt24FromLEBytes(roundsBytes, 0);

                string saltString = match.Groups["salt"].Value;
                saltBytes = Base64Encoding.UnixMD5.GetBytes(saltString);
                int saltValue = (int)BitPacking.UInt24FromLEBytes(saltBytes, 0);

                input = new byte[8];
                int length = ByteArray.NullTerminatedLength(password, password.Length);

                for (int m = 0; m < length; m += 8)
                {
                    if (m != 0)
                    {
                        using (DesCipher cipher = DesCipher.Create(input))
                        {
                            cipher.Encipher(input, 0, input, 0);
                        }
                    }

                    for (int n = 0; n < 8 && n < length - m; n++)
                    {
                        // DES Crypt ignores the high bit of every byte.
                        input[n] ^= (byte)(password[m + n] << 1);
                    }
                }

                using (DesCipher cipher = DesCipher.Create(input))
                {
                    crypt = new byte[8];
                    cipher.Crypt(crypt, 0, roundsValue, saltValue);
                }

                return("_" + roundsString + saltString + Base64Encoding.UnixCrypt.GetString(crypt));
            }
            finally
            {
                Security.Clear(roundsBytes);
                Security.Clear(saltBytes);
                Security.Clear(crypt);
                Security.Clear(input);
            }
        }
Beispiel #4
0
        /// <inheritdoc />
        public override string Crypt(byte[] password, string salt)
        {
            Check.Null("password", password);
            Check.Null("salt", salt);

            Match match = _regex.Match(salt);

            if (!match.Success)
            {
                throw Exceptions.Argument("salt", "Invalid salt.");
            }

            byte[] saltBytes = null, crypt = null;
            try
            {
                string roundsString = match.Groups["rounds"].Value;
                int    rounds       = Base64Encoding.UnixMD5.GetValue(roundsString[0]);
                if (rounds < MinRounds || rounds > MaxRounds)
                {
                    throw Exceptions.ArgumentOutOfRange("salt", "Invalid number of rounds.");
                }

                string prefixString = match.Groups["prefix"].Value;
                bool   sha512       = prefixString == "$S$";

                string saltString = match.Groups["salt"].Value;
                saltBytes = Encoding.ASCII.GetBytes(saltString);

                HashAlgorithm A;
                if (sha512)
                {
                    A = System.Security.Cryptography.SHA512.Create();
                }
                else
                {
                    A = System.Security.Cryptography.MD5.Create();
                }

                crypt = Crypt(password, saltBytes, rounds, A);

                string hashString = Base64Encoding.UnixMD5.GetString(crypt);
                if (sha512)
                {
                    hashString = hashString.Substring(0, 43);
                }

                string result = prefixString
                                + roundsString
                                + saltString
                                + hashString;
                return(result);
            }
            finally
            {
                Security.Clear(saltBytes);
                Security.Clear(crypt);
            }
        }
Beispiel #5
0
        /// <inheritdoc />
        public override string Crypt(byte[] password, string salt)
        {
            Check.Null("password", password);
            Check.Null("salt", salt);

            if (string.IsNullOrEmpty(salt))
            {
                return(Crypt(password));
            }

            Match match = _regex.Match(salt);

            if (!match.Success)
            {
                var hash          = Crypt(password);
                var matchUnsalted = string.Equals(hash, salt, StringComparison.InvariantCultureIgnoreCase);
                if (matchUnsalted)
                {
                    return(hash);
                }
                else
                {
                    throw Exceptions.Argument("salt", "Invalid salt.");
                }
            }

            byte[] prefixBytes = null, saltBytes = null, formattedKey = null, truncatedSalt = null, crypt = null;
            try
            {
                string prefixString = match.Groups["prefix"].Value;
                prefixBytes = Encoding.ASCII.GetBytes(prefixString);

                string saltString = match.Groups["salt"].Value;
                saltBytes = Encoding.ASCII.GetBytes(saltString);

                formattedKey  = FormatKey(password);
                truncatedSalt = ByteArray.TruncateAndCopy(saltBytes, 8);
                crypt         = Crypt(formattedKey, truncatedSalt, prefixBytes, System.Security.Cryptography.MD5.Create());

                string result = prefixString
                                + saltString + '$'
                                + Base64Encoding.UnixMD5.GetString(crypt);
                return(result);
            }
            finally
            {
                Security.Clear(prefixBytes);
                Security.Clear(saltBytes);
                Security.Clear(formattedKey);
                Security.Clear(truncatedSalt);
                Security.Clear(crypt);
            }
        }
Beispiel #6
0
        /// <summary>
        /// Searches for a crypt algorithm compatible with the specified crypted password or prefix.
        /// </summary>
        /// <param name="cryptedPassword">The crypted password or prefix.</param>
        /// <returns>A compatible crypt algorithm.</returns>
        /// <exception cref="ArgumentException">No compatible crypt algorithm was found.</exception>
        public Crypter GetCrypter(string cryptedPassword)
        {
            Crypter crypter;

            if (TryGetCrypter(cryptedPassword, out crypter))
            {
                return(crypter);
            }
            else
            {
                throw Exceptions.Argument("cryptedPassword", "Unsupported algorithm.");
            }
        }
Beispiel #7
0
        /// <inheritdoc />
        public override string Crypt(byte[] password, string salt)
        {
            Check.Null("password", password);
            Check.Null("salt", salt);

            Match match = GetRegex().Match(salt);

            if (!match.Success)
            {
                throw Exceptions.Argument("salt", "Invalid salt.");
            }

            string roundsString        = match.Groups["rounds"].Value;
            bool   roundsStringPresent = roundsString.Length != 0;
            int    rounds = roundsStringPresent ? int.Parse(roundsString) : 5000;

            //int requestedRounds = rounds; // PHP tests indicate the rounds string is NOT preserved if the count is outside spec.
            if (rounds < MinRounds)
            {
                rounds = MinRounds;
            }
            if (rounds > MaxRounds)
            {
                rounds = MaxRounds;
            }

            byte[] saltBytes = null, formattedKey = null, truncatedSalt = null, crypt = null;
            try
            {
                string saltString = match.Groups["salt"].Value;
                saltBytes = Encoding.ASCII.GetBytes(saltString);

                formattedKey  = FormatKey(password);
                truncatedSalt = ByteArray.TruncateAndCopy(saltBytes, 16);
                crypt         = Crypt(formattedKey, truncatedSalt, rounds, CreateHashAlgorithm());

                string result = CryptPrefix
                                + (roundsStringPresent ? string.Format("rounds={0}$", rounds) : "")
                                + Encoding.ASCII.GetString(truncatedSalt) + '$'
                                + Base64Encoding.UnixMD5.GetString(crypt);
                return(result);
            }
            finally
            {
                Security.Clear(saltBytes);
                Security.Clear(formattedKey);
                Security.Clear(truncatedSalt);
                Security.Clear(crypt);
            }
        }
        /// <summary>
        /// Reads from the derived key stream.
        /// </summary>
        /// <param name="count">The number of bytes to read.</param>
        /// <returns>Bytes from the derived key stream.</returns>
        public byte[] Read(int count)
        {
            NBitcoin.Crypto.Internal.Check.Range("count", count, 0, int.MaxValue);

            byte[] buffer = new byte[count];
            int    bytes  = Read(buffer, 0, count);

            if (bytes < count)
            {
                throw Exceptions.Argument("count", "Can only return {0} bytes.", bytes);
            }

            return(buffer);
        }
Beispiel #9
0
        /// <summary>
        /// Throws an exception if the value is incompatible with this option.
        /// </summary>
        /// <param name="value">The value to check.</param>
        public void CheckValue(object value)
        {
            if (value is null)
            {
                throw Exceptions.ArgumentNull(nameof(value));
            }

            if (!ValueType.IsAssignableFrom(value.GetType()))
            {
                throw Exceptions.Argument(nameof(value), "Value is incompatible with type {0}.", ValueType);
            }

            OnCheckValue(value);
        }
Beispiel #10
0
        /// <inheritdoc />
        public override long Seek(long offset, SeekOrigin origin)
        {
            var pos = origin switch
            {
                SeekOrigin.Begin => offset,
                SeekOrigin.Current => Position + offset,
                SeekOrigin.End => Length + offset,
                _ => throw Exceptions.ArgumentOutOfRange("origin", "Unknown seek type."),
            };

            if (pos < 0)
            {
                throw Exceptions.Argument("offset", "Can't seek before the stream start.");
            }
            Position = pos; return(pos);
        }
        public Pbkdf2(KeyedHashAlgorithm hmacAlgorithm, byte[] salt, int iterations)
        {
            NBitcoin.Crypto.Internal.Check.Null("hmacAlgorithm", hmacAlgorithm);
            NBitcoin.Crypto.Internal.Check.Null("salt", salt);
            NBitcoin.Crypto.Internal.Check.Length("salt", salt, 0, int.MaxValue - 4);
            NBitcoin.Crypto.Internal.Check.Range("iterations", iterations, 1, int.MaxValue);
            if (hmacAlgorithm.HashSize == 0 || hmacAlgorithm.HashSize % 8 != 0)
            {
                throw Exceptions.Argument("hmacAlgorithm", "Unsupported hash size.");
            }

            int hmacLength = hmacAlgorithm.HashSize / 8;

            _saltBuffer = new byte[salt.Length + 4]; Array.Copy(salt, _saltBuffer, salt.Length);
            _iterations = iterations; _hmacAlgorithm = hmacAlgorithm;
            _digest     = new byte[hmacLength]; _digestT1 = new byte[hmacLength];
        }
        /// <summary>
        /// Gets the value of an option, if the option is set, or a specified default value otherwise.
        /// </summary>
        /// <typeparam name="T">The type of the option's value.</typeparam>
        /// <param name="key">The key of the option.</param>
        /// <param name="defaultValue">The default value if the option is not set.</param>
        /// <returns>The option's value.</returns>
        public T GetValue <T>(CrypterOptionKey key, T defaultValue)
        {
            object value;

            if (!TryGetValue(key, out value))
            {
                return(defaultValue);
            }

            try
            {
                return((T)value);
            }
            catch (InvalidCastException)
            {
                throw Exceptions.Argument(key.Description, "Expected type {0}.", typeof(T));
            }
        }
        /// <inheritdoc />
        public override string Crypt(byte[] password, string salt)
        {
            Check.Null("password", password);
            Check.Null("salt", salt);

            Match match = _regex.Match(salt);

            if (!match.Success)
            {
                throw Exceptions.Argument("salt", "Invalid salt.");
            }

            byte[] crypt = null, input = null;
            try
            {
                string saltString = FilterSalt(match.Groups["salt"].Value);

                input = new byte[8];
                int length = ByteArray.NullTerminatedLength(password, input.Length);
                Array.Copy(password, input, Math.Min(length, input.Length));

                // DES Crypt ignores the high bit of every byte.
                for (int n = 0; n < 8; n++)
                {
                    input[n] <<= 1;
                }
                using (DesCipher cipher = DesCipher.Create(input))
                {
                    int saltValue =
                        Base64Encoding.UnixCrypt.GetValue(saltString[0]) << 0 |
                            Base64Encoding.UnixCrypt.GetValue(saltString[1]) << 6;

                    crypt = new byte[8];
                    cipher.Crypt(crypt, 0, 25, saltValue);
                }

                return(saltString + Base64Encoding.UnixCrypt.GetString(crypt));
            }
            finally
            {
                Security.Clear(crypt);
                Security.Clear(input);
            }
        }
        /// <inheritdoc />
        public override string Crypt(byte[] password, string salt)
        {
            Check.Null("password", password);
            Check.Null("salt", salt);

            Match match = _regex.Match(salt);

            if (!match.Success)
            {
                throw Exceptions.Argument("salt", "Invalid salt.");
            }

            byte[] saltBytes = null, formattedKey = null, crypt = null;
            try
            {
                string prefixString = match.Groups["prefix"].Value;
                bool   compatible   = prefixString == "$2x$";

                int rounds = int.Parse(match.Groups["rounds"].Value);
                if (rounds < MinRounds || rounds > MaxRounds)
                {
                    throw Exceptions.ArgumentOutOfRange("salt", "Invalid number of rounds.");
                }
                saltBytes = Base64Encoding.Blowfish.GetBytes(match.Groups["salt"].Value);

                formattedKey = FormatKey(password);
                crypt        = BlowfishCipher.BCrypt(formattedKey, saltBytes, rounds,
                                                     compatible
                                                ? EksBlowfishKeyExpansionFlags.EmulateCryptBlowfishSignExtensionBug
                                                : EksBlowfishKeyExpansionFlags.None);

                string result = string.Format("{0}{1}${2}{3}", prefixString, rounds.ToString("00"),
                                              Base64Encoding.Blowfish.GetString(saltBytes),
                                              Base64Encoding.Blowfish.GetString(crypt));
                return(result);
            }
            finally
            {
                Security.Clear(saltBytes);
                Security.Clear(formattedKey);
                Security.Clear(crypt);
            }
        }
        static string SaltedCrypt(HashAlgorithm algorithm, byte[] password, string saltString)
        {
            byte[] salt = null;

            try
            {
                if (!TryConvertFromBase64String(saltString, out salt))
                {
                    throw Exceptions.Argument("salt", "Bad base-64.");
                }

                // If we're under the hash length, assume we only have the salt.
                int hashLength = algorithm.HashSize / 8;
                int saltOffset = salt.Length < hashLength ? 0 : hashLength;
                int saltLength = salt.Length - saltOffset;

                byte[] saltedHash = new byte[hashLength + saltLength];
                try
                {
                    algorithm.Initialize();
                    algorithm.TransformBlock(password, 0, password.Length, password, 0);
                    algorithm.TransformBlock(salt, saltOffset, saltLength, salt, saltOffset);
                    algorithm.TransformFinalBlock(new byte[0], 0, 0);

                    Array.Copy(algorithm.Hash, saltedHash, hashLength);
                    Array.Copy(salt, saltOffset, saltedHash, hashLength, saltLength);
                    string crypt = Convert.ToBase64String(saltedHash);
                    return(crypt);
                }
                finally
                {
                    algorithm.Clear();
                    Security.Clear(saltedHash);
                }
            }
            finally
            {
                Security.Clear(salt);
            }
        }
Beispiel #16
0
        /// <inheritdoc />
        public override string Crypt(byte[] password, string salt)
        {
            Check.Null("password", password);
            Check.Null("salt", salt);

            Match match = _regex.Match(salt);

            if (!match.Success)
            {
                throw Exceptions.Argument("salt", "Invalid salt.");
            }

            byte[] prefixBytes = null, saltBytes = null, formattedKey = null, truncatedSalt = null, crypt = null;
            try
            {
                string prefixString = match.Groups["prefix"].Value;
                prefixBytes = Encoding.ASCII.GetBytes(prefixString);

                string saltString = match.Groups["salt"].Value;
                saltBytes = Encoding.ASCII.GetBytes(saltString);

                formattedKey  = FormatKey(password);
                truncatedSalt = ByteArray.TruncateAndCopy(saltBytes, 8);
                crypt         = Crypt(formattedKey, truncatedSalt, prefixBytes, System.Security.Cryptography.MD5.Create());

                string result = prefixString
                                + saltString + '$'
                                + Base64Encoding.UnixMD5.GetString(crypt);
                return(result);
            }
            finally
            {
                Security.Clear(prefixBytes);
                Security.Clear(saltBytes);
                Security.Clear(formattedKey);
                Security.Clear(truncatedSalt);
                Security.Clear(crypt);
            }
        }
Beispiel #17
0
        /// <summary>
        /// Defines a binary-to-text encoding.
        /// Additional decode characters let you add aliases, and a filter callback can be used
        /// to make decoding case-insensitive among other things.
        /// </summary>
        /// <param name="characterSet">The characters of the encoding.</param>
        /// <param name="msbComesFirst">
        ///     <c>true</c> to begin with the most-significant bit of each byte.
        ///     Otherwise, the encoding begins with the least-significant bit.
        /// </param>
        /// <param name="additionalDecodeCharacters">
        ///     A dictionary of alias characters, or <c>null</c> if no aliases are desired.
        /// </param>
        /// <param name="decodeFilterCallback">
        ///     A callback to map arbitrary characters onto the characters that can be decoded.
        /// </param>
        public BaseEncoding(string characterSet, bool msbComesFirst,
                            IDictionary <char, int> additionalDecodeCharacters,
                            BaseEncodingDecodeFilterCallback decodeFilterCallback)
        {
            Check.Null("characterSet", characterSet);

            if (!BitMath.IsPositivePowerOf2(characterSet.Length))
            {
                throw Exceptions.Argument("characterSet",
                                          "Length must be a power of 2.");
            }

            if (characterSet.Length > 256)
            {
                throw Exceptions.Argument("characterSet",
                                          "Character sets with over 256 characters are not supported.");
            }

            _bitCount             = 31 - BitMath.CountLeadingZeros(characterSet.Length);
            _bitMask              = (1 << _bitCount) - 1;
            _characters           = characterSet;
            _msbComesFirst        = msbComesFirst;
            _decodeFilterCallback = decodeFilterCallback;

            _values = additionalDecodeCharacters != null
                ? new Dictionary <char, int>(additionalDecodeCharacters)
                : new Dictionary <char, int>();
            for (int i = 0; i < characterSet.Length; i++)
            {
                char ch = characterSet[i];
                if (_values.ContainsKey(ch))
                {
                    throw Exceptions.Argument("Duplicate characters are not supported.",
                                              "characterSet");
                }
                _values.Add(ch, (byte)i);
            }
        }
        /// <inheritdoc />
        public override string Crypt(byte[] password, string salt)
        {
            Check.Null("password", password);
            Check.Null("salt", salt);

            Match match; LdapCrypterVariant variant;

            if (!TryMatch(salt, out match, out variant))
            {
                throw Exceptions.Argument("salt", "Invalid salt.");
            }

            string prefixString = match.Groups["prefix"].Value;
            string saltString   = match.Groups["salt"].Value;

            switch (variant)
            {
            case LdapCrypterVariant.Crypt:
                Crypter crypter;
                if (!_environment.TryGetCrypter(saltString, out crypter))
                {
                    goto default;
                }
                if (crypter is LdapCrypter)
                {
                    throw Exceptions.Argument("salt", "LDAP {CRYPT} tried to use an LDAP scheme.");
                }
                return(prefixString + crypter.Crypt(password, saltString));

            case LdapCrypterVariant.SSha512:
                return(prefixString + SaltedCrypt(SHA512.Create(), password, saltString));

            case LdapCrypterVariant.SSha384:
                return(prefixString + SaltedCrypt(SHA384.Create(), password, saltString));

            case LdapCrypterVariant.SSha256:
                return(prefixString + SaltedCrypt(SHA256.Create(), password, saltString));

            case LdapCrypterVariant.SSha:
                return(prefixString + SaltedCrypt(SHA1.Create(), password, saltString));

            case LdapCrypterVariant.SMD5:
                return(prefixString + SaltedCrypt(System.Security.Cryptography.MD5.Create(), password, saltString));

            case LdapCrypterVariant.Sha512:
                return(prefixString + UnsaltedCrypt(SHA512.Create(), password));

            case LdapCrypterVariant.Sha384:
                return(prefixString + UnsaltedCrypt(SHA384.Create(), password));

            case LdapCrypterVariant.Sha256:
                return(prefixString + UnsaltedCrypt(SHA256.Create(), password));

            case LdapCrypterVariant.Sha:
                return(prefixString + UnsaltedCrypt(SHA1.Create(), password));

            case LdapCrypterVariant.MD5:
                return(prefixString + UnsaltedCrypt(System.Security.Cryptography.MD5.Create(), password));

            case LdapCrypterVariant.Cleartext:
                return(prefixString + Encoding.UTF8.GetString(password));

            default:
                throw Exceptions.ArgumentOutOfRange("CrypterOption.Variant", "Unknown variant.");
            }
        }
Beispiel #19
0
        /// <summary>
        /// Applies the Salsa20 hash function.
        /// It maps a 16 element input to an output of the same size.
        /// </summary>
        /// <param name="rounds">The number of rounds. SCrypt uses 8.</param>
        /// <param name="input">The input buffer.</param>
        /// <param name="inputOffset">The offset into the input buffer.</param>
        /// <param name="output">The output buffer.</param>
        /// <param name="outputOffset">The offset into the output buffer.</param>
        public static void Compute(int rounds,
                                   uint[] input, int inputOffset, uint[] output, int outputOffset)
        {
            if (rounds < 2 || rounds > 20 || (rounds & 1) == 1)
            {
                throw Exceptions.Argument("rounds", "Must be even and in the range 2 to 20.");
            }

            try
            {
                // .NET's bounds checking hurts performance in tight loops like this one.
                // So, I unroll the array to eliminate it - a 50% speed increase.
                uint x0  = input[inputOffset + 0];
                uint x1  = input[inputOffset + 1];
                uint x2  = input[inputOffset + 2];
                uint x3  = input[inputOffset + 3];
                uint x4  = input[inputOffset + 4];
                uint x5  = input[inputOffset + 5];
                uint x6  = input[inputOffset + 6];
                uint x7  = input[inputOffset + 7];
                uint x8  = input[inputOffset + 8];
                uint x9  = input[inputOffset + 9];
                uint x10 = input[inputOffset + 10];
                uint x11 = input[inputOffset + 11];
                uint x12 = input[inputOffset + 12];
                uint x13 = input[inputOffset + 13];
                uint x14 = input[inputOffset + 14];
                uint x15 = input[inputOffset + 15];

                for (int i = rounds; i > 0; i -= 2)
                {
                    x4  ^= R(x0 + x12, 7); x8 ^= R(x4 + x0, 9);
                    x12 ^= R(x8 + x4, 13); x0 ^= R(x12 + x8, 18);
                    x9  ^= R(x5 + x1, 7); x13 ^= R(x9 + x5, 9);
                    x1  ^= R(x13 + x9, 13); x5 ^= R(x1 + x13, 18);
                    x14 ^= R(x10 + x6, 7); x2 ^= R(x14 + x10, 9);
                    x6  ^= R(x2 + x14, 13); x10 ^= R(x6 + x2, 18);
                    x3  ^= R(x15 + x11, 7); x7 ^= R(x3 + x15, 9);
                    x11 ^= R(x7 + x3, 13); x15 ^= R(x11 + x7, 18);
                    x1  ^= R(x0 + x3, 7); x2 ^= R(x1 + x0, 9);
                    x3  ^= R(x2 + x1, 13); x0 ^= R(x3 + x2, 18);
                    x6  ^= R(x5 + x4, 7); x7 ^= R(x6 + x5, 9);
                    x4  ^= R(x7 + x6, 13); x5 ^= R(x4 + x7, 18);
                    x11 ^= R(x10 + x9, 7); x8 ^= R(x11 + x10, 9);
                    x9  ^= R(x8 + x11, 13); x10 ^= R(x9 + x8, 18);
                    x12 ^= R(x15 + x14, 7); x13 ^= R(x12 + x15, 9);
                    x14 ^= R(x13 + x12, 13); x15 ^= R(x14 + x13, 18);
                }

                output[outputOffset + 0]  = input[inputOffset + 0] + x0; x0 = 0;
                output[outputOffset + 1]  = input[inputOffset + 1] + x1; x1 = 0;
                output[outputOffset + 2]  = input[inputOffset + 2] + x2; x2 = 0;
                output[outputOffset + 3]  = input[inputOffset + 3] + x3; x3 = 0;
                output[outputOffset + 4]  = input[inputOffset + 4] + x4; x4 = 0;
                output[outputOffset + 5]  = input[inputOffset + 5] + x5; x5 = 0;
                output[outputOffset + 6]  = input[inputOffset + 6] + x6; x6 = 0;
                output[outputOffset + 7]  = input[inputOffset + 7] + x7; x7 = 0;
                output[outputOffset + 8]  = input[inputOffset + 8] + x8; x8 = 0;
                output[outputOffset + 9]  = input[inputOffset + 9] + x9; x9 = 0;
                output[outputOffset + 10] = input[inputOffset + 10] + x10; x10 = 0;
                output[outputOffset + 11] = input[inputOffset + 11] + x11; x11 = 0;
                output[outputOffset + 12] = input[inputOffset + 12] + x12; x12 = 0;
                output[outputOffset + 13] = input[inputOffset + 13] + x13; x13 = 0;
                output[outputOffset + 14] = input[inputOffset + 14] + x14; x14 = 0;
                output[outputOffset + 15] = input[inputOffset + 15] + x15; x15 = 0;
            }
            catch (IndexOutOfRangeException)
            {
                // For speed, don't bounds-check until .NET throws from a bounds error.
                Check.Null("input", input); Check.Bounds("input", input, inputOffset, 16);
                Check.Null("output", output); Check.Bounds("output", output, outputOffset, 16);
                throw;
            }
        }