Introducing PassRoot
Determining determistic passwords diabolically
I recently published my first public web application, called PassRoot. It’s basically a stateless password manager. No encrypted database to hold onto, no logging into an account somewhere. Just plug in a master passphrase, username, and domain name and out pops a secure password calculated deterministically.
I built it because I caught myself re-using passwords. Most security experts will tell you that’s bad. And they are correct! There is nothing preventing web services from taking your password and storing it in plain text, or as terrible hash that is easy to crack. Heck, there is nothing preventing web services from just collecting your credentials and selling them out the back door. And websites get hacked all the time. Sooner or later that one password you use for everything will end up on a public list.
But there are just too many damn websites that want your login info. It would be downright impossible to come up with good unique passwords for each one and actually remember them. Which is just one of many reasons passwords are broken and we should, as a society, move past passwords. But in the meantime, you should use a password manager.
But most password managers make me nervous. Most password managers generate purely random password strings, which is a good start, but then you have to store that somewhere. If it’s on your local device, then you can’t access the password on other devices which will come back to bite you. And you have to worry about a hard drive crash, or it getting stolen, or just dropping your phone in the toilet.
If it’s in the cloud…it’s in the friggin cloud! Your most sensitive credentials are on someone else’s computer!
- I want at least the possibility of offline access. I don’t trust the uptime of any single service.
- I don’t think I can fully trust any promise that the service itself can’t access my passwords, regardless of encrypted vaults.
Are you telling me neither yourself nor anyone else can access the passwords I upload to your server?
So I solved it differently. With math. The basic idea is to feed the inputs into a hashing algorithm so it creates “random” bytes and use that to encode a usable password. PassRoot isn’t the first app to do this, but I do believe it is the best. Or at least the few I came across worry me for a different reason: they are fast. I found one that actually generated new passwords instantly as fast as you type into the input fields. And that’s a bad thing.
Generating passwords from fast hashes is bad for the same reason that storing passwords as fast hashes is bad. If you can generate a hash fast, so can an attacker. With a hash to compare against they will make as many guesses per second as their hardware allows. The difference between a good password hash and a poor standard hash is thousands of guesses per second versus billions of guesses per second.
In this case the generated “password” is essentially just a hash of the master password. Any leak of a generated password is a leak of the hash of the master password. Normally if a website leaks a hash of a password you use in multiple places, that is a major cause for concern. But thankfully it’s possible to make the hash calculation so incredibly brutal that it simply does not matter.
The best password hashing algorithms (Argon2 and bcrypt) have configurable “cost” parameters. Argon2 is especially configurable, with knobs for the output hash size, a “type”, memory cost, parallelism, and iterations. Memory cost especially seems to have the effect of resisting highly parallelized GPU calculations. The general advice for configuring the cost is just to test in-situ and pump it up as far as you stomach. A web server probably shouldn’t pump it up past eating 0.1 seconds of time or all that much memory. Otherwise you might risk denial-of-service. But for a password generator I have the luxury of eating as much time as the average user is willing to wait for a result on a web page. So I aimed for 2 seconds on a modern smartphone, plus a gut-wrenching 512 MiB of memory. That kind of hurts to say, honestly. Most of the time I prefer to write code that would outwardly perform just as well on a beige box from 1998 as it does today. But here it’s important to be as brutal as you can manage. Most browsers can manage to take up 512 MiB in a single tab (some websites do!), therefore it should take up that amount. Most people can wait two full seconds for a result, and therefore it should take up two full seconds.
The more resource intensive the calculation is the less “guesses per second” an attacker has. An attacker also has to guess a sizeable portion of the space of possible passwords, 50% of it to have a likelihood. So we can tweak two knobs:
- Increase the bits of entropy in the password, which makes it more complex and difficult to remember.
- Decrease the guesses per second (by increasing the hashing time) which costs time to wait.
I don’t have access to a top of the line GPU in order to run benchmarks, but I did find some numbers for benchmarking some less brutal configurations on an RTX 5090. Apparently an RTX 5090 can do 178 hashes per second against Argon2 with 256 MiB memory and 3 iterations. PassRoot uses 512 MiB of memory and 4 iterations. Judging by those numbers, I’m confident the “guesses per second” for PassRoot will be well under a thousand per second for quite some time.
Let’s assume an attacker with hilarious resources - they can do 1 thousand per second per GPU and they have a million GPU datacenter. If you build a pass phrase out of 6 random words at 13 bits each (78 bits of entropy) it will take 4.79 million years for the attacker to have a likelihood of cracking the master password.
So with PassRoot I encourage people to memorize a 6 word pass phrase just to get into “insanely secure” range. But theoretically you can pump up the calculation time and get away with simpler passwords. Most of the time this is a fairly rare event. It’s fine to save your passwords in your browser - you just need this for signup or recovery if you lost access to the saved password. So maybe you could wait a minute? An hour? A day? How simple of a master passphrase could you get away with? As part of this project I made a pass phrase generator and corresponding cracking time calculator. It’s fun to play around with the math.
Let’s bring the attacker power down to a single GPU, and hash time up to 100 guesses per second. 100 per second is more realistic for how PassRoot probably performs today in an optimized setting. With a longer wordlist (14.10 bits per word) you could get away with a 3 word pass phrase, resulting in 8.58 centuries for a likely crack.
If you can manage to make the hash calculation take 1 second, it would still take almost 5 years to crack a two word passphrase. It does take 2 whole seconds on a smartphone CPU to calculate PassRoot, but it’s difficult to predict how fast a GPU could muster in comparison. It’s likely an attacker can do better than 1 per second, so please don’t consider using a two word pass phrase with PassRoot. These calculations are just for fun.
Let’s see if you could get away with a single word, just 14.10 bits
of entropy. You would need 0.0001 guesses per second to get 2.78 years
against a single GPU attacker, which seems reasonable enough to me. So
that’s 1/0.0001/60/60 ~= 2.77 hours. So if you can wait a
few hours you could have a single word as your passphrase! As your
master passphrase, for literally everything you need only a
single word and a few hours to kill. Not something I would really
recommend, but hey.
- Published
- 2026-06-01
- Updated
- 2026-06-01