PasswordHasher is a class for creating Argon2 hashes and verifying them. This is a wrapper around Daniel Dinu and Dmitry Khovratovich's Argon2 library.
Inheritance: IPasswordHasher
Esempio n. 1
0
        private static void Run(PasswordHasher hasher, string pwd, string salt, bool rawOnly, bool encodedOnly)
        {
            try
            {
                if (rawOnly)
                {
                    Console.WriteLine(ToHex(hasher.HashRaw(pwd, salt)));
                    return;
                }

                if (encodedOnly)
                {
                    Console.WriteLine(hasher.Hash(pwd, salt));
                    return;
                }

                var startTime = DateTime.Now;
                string encoded = hasher.Hash(pwd, salt);
                var stopTime = DateTime.Now;
                
                HashMetadata metadata = PasswordHasher.ExtractMetadata(encoded);

                Console.WriteLine("Hash:\t\t" + ToHex(metadata.Hash));
                Console.WriteLine("Encoded:\t" + encoded);
                Console.WriteLine("{0:0.000} seconds", (stopTime - startTime).TotalSeconds);

                if(hasher.Verify(encoded, pwd))
                    Console.WriteLine("Verification ok");
                else
                    throw new Argon2Exception("verifying", Argon2Error.VERIFY_MISMATCH);
            }
            catch (Exception ex)
            {
                Fatal(ex.Message);
            }
        }
Esempio n. 2
0
        // This code is more complicated than necessary IF you do not care about attackers enumerating your user list via timing attacks
        public ActionResult Login(string username, string password)
        {
            // Do profiling to determine what the optimal time and memory cost would be for your server setup
            // Ideally, PasswordHasher.Hash should take about 200ms to perform as a good balance between security and responsiveness
            // Prefer to increase MemoryCost instead of TimeCost (increasing MemoryCost will also increase the overall time)
            // TODO: Edit your web.config or app.config to contain the following keys within your <configuration><appSettings>...</appSettings></configuration>:
            // TODO: <add key="PasswordHasher.TimeCost" value="3" />
            // TODO: <add key="PasswordHasher.MemoryCost" value="65536" />
            // If you are updating the time cost or memory cost (because, e.g., you moved to faster hardware) then 
            // add PasswordHasher.OldTimeCost and PasswordHasher.OldMemoryCost keys with the previous values to ensure timing attacks cannot
            // be done to enumerate the possible users. Remove OldTimeCost and OldMemoryCost when the migration is finished.

            string timeCostStr = (ConfigurationManager.AppSettings["PasswordHasher.TimeCost"] ?? "3");
            string memoryCostStr = (ConfigurationManager.AppSettings["PasswordHasher.MemoryCost"] ?? "65536");
            uint timeCost = uint.Parse(timeCostStr);
            uint memoryCost = uint.Parse(memoryCostStr);
            uint oldTimeCost = uint.Parse(ConfigurationManager.AppSettings["PasswordHasher.OldTimeCost"] ?? timeCostStr);
            uint oldMemoryCost = uint.Parse(ConfigurationManager.AppSettings["PasswordHasher.OldMemoryCost"] ?? memoryCostStr);

            bool costsDiffer = (timeCost != oldTimeCost || memoryCost != oldMemoryCost);

            string hashFormat = "$argon2i$m={0},t={1},p=1$AAAAAAAAAAAAAAAAAAAAAA$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
            string dummyHash = string.Format(hashFormat, memoryCost, timeCost);
            string oldDummyHash = string.Format(hashFormat, oldMemoryCost, oldTimeCost);

            var passwordHasher = new PasswordHasher(timeCost, memoryCost);
            
            // Get the user from the database in hopefully some constant-time fashion so that timing attacks
            // cannot be used to enumerate valid usernames
            User user = GetUser(username);
            string passwordHash = (user != null ? user.PasswordHash : dummyHash);
            bool updatedCost;
            string newPasswordHash;

            HashMetadata hashMetadata = PasswordHasher.ExtractMetadata(passwordHash);
            bool usingOldCosts = (hashMetadata.MemoryCost != memoryCost || hashMetadata.TimeCost != timeCost);

            // always compare against a password hash even if the user is not found (when that happens, we compare against dummyHash)
            // to prevent timing attacks from enumerating a list of valid usernames
            if (passwordHasher.VerifyAndUpdate(passwordHash, password, out updatedCost, out newPasswordHash))
            {
                // VerifyAndUpdate will generate a new password hash if the time or memory cost from ConfigurationManager.AppSettings
                // does not match what was used to generate the password hash which was stored in the database.
                // This allows you to easily update the password hashing cost if you upgrade to better server hardware by
                // modifying the PasswordHasher.TimeCost and PasswordHasher.MemoryCost parameters.
                if (updatedCost)
                {
                    // As users successfully log in, update their password hash to the hash with the updated cost
                    user.PasswordHash = newPasswordHash;
                    UpdateUser(user);
                }
                else if(costsDiffer)
                {
                    // run the verification with the old costs so that each login attempt performs a hash
                    // with the new costs and with the old costs to ensure the timing is consistent
                    passwordHasher.Verify(oldDummyHash, password);
                }

                // Successful login
                LogInUser(user);
                return RedirectToAction("Index", "Home");
            }

            // if we're migrating password hashes from the old cost parameters to new cost parameters, we want
            // each login attempt to perform a single hash with the new cost parameters and a single hash with
            // the old cost parameters so that the timing is consistent.
            if(costsDiffer)
                passwordHasher.Verify((usingOldCosts ? dummyHash : oldDummyHash), password);

            // User failed to login
            var model = new LoginViewModel {ErrorMessage = "Username or Password is incorrect."};
            return View(model);
        }
Esempio n. 3
0
        static void Main(string[] args)
        {
            uint m_cost = (uint)(1 << LOG_M_COST_DEF);
            uint t_cost = T_COST_DEF;
            uint threads = THREADS_DEF;
            uint hash_len = HASH_LEN_DEF;
            Argon2Type type = Argon2Type.Argon2i;
            bool rawOnly = false;
            bool encodedOnly = false;

            if (args.Length == 0)
            {
                Usage();
                Environment.Exit(30);
            }
            
            var pwd = Console.In.ReadToEnd();

            if (pwd.EndsWith("\r\n"))
                pwd = pwd.Substring(0, pwd.Length - 2);
            else if(pwd.EndsWith("\n"))
                pwd = pwd.Substring(0, pwd.Length - 1);

            if (args[0].Length > SALT_LEN)
                Fatal("salt too long");

            var salt = args[0] + new string('\0', SALT_LEN - args[0].Length);

            for (var i = 1; i < args.Length; i++)
            {
                switch (args[i])
                {
                    case "-m": m_cost = (1U << (int)ReadArg(args, ++i, "-m", 1, 32)); break;
                    case "-t": t_cost = ReadArg(args, ++i, "-t", 1, int.MaxValue); break;
                    case "-p": threads = ReadArg(args, ++i, "-p", 1, 0xFFFFFF); break;
                    case "-h": hash_len = ReadArg(args, ++i, "-h", 4, int.MaxValue); break;
                    case "-d": type = Argon2Type.Argon2d; break;
                    case "-e":
                    case "-encoded":
                        encodedOnly = true;
                        break;
                    case "-r":
                    case "-raw":
                        rawOnly = true;
                        break;
                    default:
                        Fatal("unknown argument " + args[i]);
                        break;
                }
            }

            if (encodedOnly && rawOnly)
                Fatal("Only one of -e or -r may be specified");

            if (!encodedOnly && !rawOnly)
            {
                Console.WriteLine("Type:\t\t{0}", type);
                Console.WriteLine("Iterations:\t{0}", t_cost);
                Console.WriteLine("Memory:\t\t{0} KiB", m_cost);
                Console.WriteLine("Parallelism:\t{0}", threads);

            }

            var hasher = new PasswordHasher(t_cost, m_cost, threads, type, hash_len);
            Run(hasher, pwd, salt, rawOnly, encodedOnly);
        }