The theory behind Master Password is simple. The user remembers a single, secure password. The user only ever uses that password to log into the Master Password application. This master password is then used as a seed to generate a different password based on the name of the site to generate a password for.
The result is that each master password generates its own unique sequence of passwords for any site name. Since the only input data is the master password and the site name (along with a password counter, see below), there is no need for any kind of storage to recreate a site's password. All that's needed is the correct master password and the correct algorithm implementation. What that does for you is make it almost impossible to lose your passwords. It also makes it nearly impossible for hackers to steal your online identity.
The user chooses a single master password, preferably sufficiently long to harden against brute-force attacks. The application then creates a scrypt key derivative from the user's password. This process takes quite a bit of processing time and memory. It makes brute-forcing the master password far more difficult, to practically infeasible, even for otherwise vulnerable password strings.
key = scrypt( P, S, N, r, p, dkLen )
where
P = master password
S = <empty>
N = 16384
r = 8
p = 1
dkLen = 64
When the user requests a password be generated for a site, the application composes a byte string consisting of the site name
(UTF-8 decoded), the key
, and a salt
(a 32-bit unsigned integer in network byte order. Normally this is the password counter), delimited in that order by a single NUL byte
, and hashes it using the SHA-1
algorithm. The result is called the seed
.
salt = htonl( password counter )
seed = sha1( site name . "\0" . key . "\0" . salt )
The seed is now combined with the password type the user has chosen for the site. Password types determine the cipher
that will be used to encode the seed
bytes into a readable password. For instance, the standard password type Long Password
activates one of three pre-set ciphers: CvcvCvcvnoCvcv
, CvcvnoCvcvCvcv
or CvcvCvcvCvcvno
. Which of those will be used, depends on the first byte of the seed
. Take the byte value modulo the amount of pre-set ciphers (in this case, three), and the result tells you which of the pre-set ciphers to use.
ciphers = [ "CvcvCvcvnoCvcv", "CvcvnoCvcvCvcv", "CvcvCvcvCvcvno" ]
cipher = ciphers[ seed[0] % count( ciphers ) ]
Now that we know what cipher to use for building our final password, all that's left is to iterate the
cipher, and produce a character of password output for each step. When you iterate the cipher (i
), every
character in the cipher represents a set of possible output characters (passChars
). For instance, a C
character in the cipher indicates that we need to choose a capital consonant character. An o
character in the cipher indicates that we need to choose an other
(symbol) character. Exactly which
character to choose in that set for the password output depends on the next byte from the seed
.
Like before, take the next unused seed
byte value modulo the amount of characters in the
set of possible output characters for the cipher iteration and use the result to choose the output
character (passChar
). Repeat until you've iterated the whole cipher.
passChar = passChars[ seed[i + 1] % count( passChars ) ]
passWord += passChar