Midnight project: Password manager without a password manager
gist.github.com
gist.github.com
http://en.wikipedia.org/wiki/Merkle%E2%80%93Damg%C3%A5rd_con...
In your system, the derived password is a sha digest of a concatentation of other sha digests. It almost has the form sha(sha(SALT) || sha(RESOURCE) || sha(MASTER)), with sha() returning the ascii encoding of the base-16 representation. (The difference is three extra newline characters "\n", which makes things slightly messier). This string representation for sha-512 digests is 128 characters or 1028 bits, and sha-512 uses 1028-bit input chunks, so the final sha() call reads the other three digests aligned on chunk boundaries. (Again, few bits off because of newlines, but this is not fatal).
So DERIVED(RESOURCE) has a form
DERIVED = sha(sha(SALT) || sha(RESOURCE) || sha(MASTER))
= f(sha(sha(SALT) || sha(RESOURCE)), g(sha(MASTER)))
where f,g, are known functions. Moreover, f is reversible -- in SHA-512 it is just addition.So for an attack: given the salt, along with one resource/derived password pair, every other password can be computed. If you know SALT, RESOURCE_1, and DERIVED_1, you know these:
sha(sha(SALT) || sha(RESOURCE_1))
DERIVED_1 = f(sha(sha(SALT) || sha(RESOURCE_1)), g(sha(MASTER)))
Because f is reversible, and you know its first argument, you also learn its other argument, g(sha(MASTER))
and can calculate any other password, given the resource name. Note you don't learn the master password, nor its sha digest -- you can't reverse the block function g(), not without breaking SHA-2 itself. But that's not necessary.If that's the case, wouldn't it suffice to mix A and B, for example with byte-wise XOR?
password ::= sha(A XOR B)Because password[0], password[1], ... password[n] are all related through common salt and master password string (and known domain name). Where as passwords stored in a password managers are independent.
Therefore, in theory, if I know a few of your passwords (lets say I own 10 top domains and you've got accounts with me), I can crack your salt and password file, or at least, I can generate probable passwords for other domains.
In the instance of this particular algorithm, the salt must be kept secret, because it is the only unknown in the process from an attackers point of view.
That means that an attacker who can somehow obtain the salt value can now mount a known-plaintext attack against the outputs of the algorithm. http://en.wikipedia.org/wiki/Known-plaintext_attack
I am ignoring the fact that in a general sense an attacker with resources to obtain the salt can also likely log the master password, in which case no attack against the algorithm is necessary.
Something like this could actually help backport a limited form of two factor auth (something I know: password + something I have: salt) to single-password systems.
That's of course barring the crypto issues this algorithm has (see comment by anon081312) but maybe things could be improved on that front (not that I want to 'roll my own', but maybe this function could be designed better, and reviewed)
What is the likelihood that you own or would have compromised 10 top domains? Not sure if that matters, just curious.
At the very least I'd personally prefer to use an open, understandable methodology to generate my passwords than some of the more popular options that are secured through obscurity.
Because password managers must store __the password__ itself (in order to be able to submit it into login forms and the like) the use of a salt for each stored password would work against the aim of storing __the passwords__ themselves.
Instead, password managers store everything in encrypted form using a master key (password) for decrypting the encrypted data file. That master password should be passed through a key stretching function ( http://en.wikipedia.org/wiki/Key_stretching ) prior to use as the encryption key for the master encryption.
If your password manager encrypts each password separately, and stores that output separately, then that simplifies the task, because you can mount a known-plaintext attack and potentially reduce the complexity.
However, if your password manager stores things correctly, all the passwords are stored as a single "blob", with no known-plaintext anywhere in the blob, and the entire blob is fed through the encryption algorithm as a single block encryption using a single key.
For example, the password safe format ( http://passwordsafe.svn.sourceforge.net/viewvc/passwordsafe/... ) the master password is both salted and passed through a work-configurable key stretch function. The result of that operation is used to encrypt a 256bit random key using the Twofish algorithm. That 256bit random key is the actual encrypt key for all the password records, again using Twofish as the encryption algorithm.
So deriving the master password is made difficult by use of the key-stretch function, and deriving the actual 256bit random key is as difficult as otherwise breaking the Twofish algorithm.
I'd certainly love to see you try to recover the master password from only 10 hashes.
http://www.mail-archive.com/cryptography@randombit.net/msg02...
So for this example, instead of:
(sha512sum ~/.salt -<<<www.example.com;sha512sum -)|cut -d\ -f1|sha512sum|cut -c-32
eg: (sha512sum ~/.salt -<<<www.example.com0;sha512sum -)|cut -d\ -f1|sha512sum|cut -c-32
which can be changed to:(sha512sum ~/.salt -<<<www.example.com1;sha512sum -)|cut -d\ -f1|sha512sum|cut -c-32
You can "rediscover" the correct password for a site, relatively easy by using this method -- iterating up to your current (correct) password. Another alternative might be using the year or month/year of last password change/set... all these methods have drawbacks -- but again see the thread for some interesting points on a similar system.
[edit: typo/formatting]
http://www.faqs.org/patents/app/20120110469#b
In our formulation, the salt was simply (a function of) your username.
But yeah, we couldn't find a way to crack it, either.
For password hashing, the input salt needs to be a cryptographically secure random number. This is because it needs to be unique, and unrelated, to each password.
In your "formulation", what you have is simply a unique identifier for a user derived from the three, but it is in no way a "salt" as that term is used in regards to "salted passwords".
> But yeah, we couldn't find a way to crack it, either.
Just because you couldn't find a way to crack it, does not mean it is secure:
http://security.stackexchange.com/questions/18197/why-should...
on a related note, we encourage each of our users to select a passphrase, and even scour yahoo news from the past year, and other sources for three consecutive words, that the user can easily remember, such as "that truck driver" or "what he did". This in practice causes users to have a much better space of possible passwords to begin with ... without which any system would be susceptible to brute force rainbow table type attacks.
And with a wordlist of the 10,000 most common words, if you are not also using a key-stretch function (bcrypt, pbkdf2, etc.) all of those examples become quite trivial to a john-the-ripper ( http://www.openwall.com/john/ ) type attack. I.e., a three word phrase consisting of one each of the 10,000 most common words has at most 10000^3 combinations (1e+12). A 10 digit random password selected from letters, numbers, punctuation (94 digits) has 94^10 combinations (5e+19). 5e+19 is significantly larger than 1e+12)
Here is what I am pretty sure is true:
1) The salt just has to be unique enough to make rainbow table attacks on any significant portion of the userbase infeasible Any given rainbow table will only work for one salt. From this perspective, usernames are just fine as a salt.
2) The real danger is password re-use (http://xkcd.com/792/) and more generally, just lousy password selection (know anyone whose password is "password"?) Pass phrases are better (http://blogs.technet.com/b/robert_hensing/archive/2004/07/28...) and if we can deliver to the users a space of 10^14 possible phrases that actually sound like they make some sense, as an inspiration for them to choose a phrase of their own, then I think that's a good thing to do.
3) And of course, we use key strengthening, running the hash function a lot of times (a prime number of times, just in case ;)
Edit: As jroes pointed out below, this is not a problem.
By using this scheme, you're trusting $domainName to securely scramble the password.
Once a single password is known in plain, an attacker has a nice password template that he can try on any site. The last few bits of security would be your user name (which is often similar to the one you use elsewhere) or nothing at all if you can log in by email address (which is usually possible these days; and that email address is often among the leaked information as well). Instant login anywhere!
At least hash that '$domain+$masterpass' string...
-----
More problems: I heard some people still truncate passwords down to unreasonable lengths on the server side, which might make hash compression (e.g. base 16 -> base 64) necessary and which will totally break the simple concatenation scheme, leaving a prefix of $domainName as the actual password. Ouch.
So, I wrote a gem: