2
0
MasterPassword/Site/2012-07/algorithm.html

446 lines
25 KiB
HTML
Raw Permalink Normal View History

<!DOCTYPE HTML>
<html>
<head>
<title>Master Password &mdash; Securing your online life.</title>
<link rel="icon" href="images/resources/favicon.png" type="image/x-png" />
<link rel="shortcut icon" href="images/resources/favicon.png" type="image/x-png" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<meta name="apple-itunes-app" content="app-id=510296984" />
<link rel='stylesheet' type='text/css' href='http://fonts.googleapis.com/css?family=Exo:100,400,600,900,100italic,400italic,600italic' />
<link rel="stylesheet" type="text/css" href="css/ml-shadows.css" />
<link rel="stylesheet" type="text/css" href="css/screen.css" />
<script src="js/jquery-1.6.1.min.js" type="text/javascript"></script>
<script src="js/functions.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$(window).scroll(function() {
if ($(window).scrollTop() > 100) {
$(".appstore").show();
$("header .appstore").hide();
} else {
$(".appstore").hide();
$("header .appstore").show();
}
})
});
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-90535-15']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<script type="text/javascript" charset="utf-8">
var is_ssl = ("https:" == document.location.protocol);
var asset_host = is_ssl ? "https://d3rdqalhjaisuu.cloudfront.net/" : "http://d3rdqalhjaisuu.cloudfront.net/";
document.write(unescape("%3Cscript src='" + asset_host + "javascripts/feedback-v2.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<!-- Get Satisfaction -->
<!--script type="text/javascript" charset="utf-8">
var is_ssl = ("https:" == document.location.protocol);
var asset_host = is_ssl ? "https://d3rdqalhjaisuu.cloudfront.net/" : "http://d3rdqalhjaisuu.cloudfront.net/";
document.write(unescape("%3Cscript src='" + asset_host + "javascripts/feedback-v2.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript" charset="utf-8">
var feedback_widget_options = {};
feedback_widget_options.display = "overlay";
feedback_widget_options.company = "lyndir";
feedback_widget_options.placement = "right";
feedback_widget_options.color = "#222";
feedback_widget_options.style = "question";
var feedback_widget = new GSFN.feedback_widget(feedback_widget_options);
</script-->
<!-- UserEcho -->
<script type='text/javascript'>
var _ues = {
host:'support.lyndir.com',
forum:'13031',
lang:'en',
tab_icon_show:false,
tab_corner_radius:5,
tab_font_size:20,
tab_image_hash:'RmVlZGJhY2s%3D',
tab_alignment:'right',
tab_text_color:'#FFFFFF',
tab_bg_color:'#DDDDDD',
tab_hover_color:'#CCCCCC'
};
(function() {
var _ue = document.createElement('script'); _ue.type = 'text/javascript'; _ue.async = true;
_ue.src = ('https:' == document.location.protocol ? 'https://s3.amazonaws.com/' : 'http://') + 'cdn.userecho.com/js/widget-1.4.gz.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(_ue, s);
})();
</script>
<!-- jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<!-- Page JS -->
<script type="text/javascript">
$(document).ready(function() {
$(window).scroll(function() {
if ($(window).scrollTop() > 100) {
$(".appstore").show();
$("header .appstore").hide();
} else {
$(".appstore").hide();
$("header .appstore").show();
}
});
$(".tip.appstore").hide().delay(1000).fadeIn();
$(".tip.phone").hide();
$("#sendtophone").mouseenter(function() {
$(".tip.phone").fadeIn();
});
$("#sendtophone").mouseleave(function() {
$(".tip.phone").fadeOut();
});
$("#sendtophone").submit(function() {
$.ajax({
url: "http://masterpassword.lyndir.com/send.php",
data: {
destination: $(this).find("input[type='text']").val(),
},
cache: false,
});
goog_report_conversion('index-sendtophone');
_gaq.push(['_trackPageview', '/outbound/sendtophone']);
$("#sendtophone .field").hide();
$("#sendtophone .confirm").show();
return false;
});
$("#sendtophone .field").show();
$("#sendtophone .confirm").hide();
});
</script>
<!-- AdWords -->
<script type="text/javascript">
/* <![CDATA[ */
goog_snippet_vars = function() {
var w = window;
w.google_conversion_id = 1015576061;
w.google_conversion_label = "PcXqCPPz5AIQ_euh5AM";
w.google_conversion_value = 4;
}
goog_report_conversion = function(url) {
goog_snippet_vars();
window.google_conversion_format = "3";
window.google_is_call = true;
var opt = new Object();
opt.onload_callback = function() {
if (typeof(url) != 'undefined') {
window.location = url;
}
}
var conv_handler = window['google_trackConversion'];
if (typeof(conv_handler) == 'function') {
conv_handler(opt);
}
}
/* ]]> */
</script>
<script type="text/javascript" src="http://www.googleadservices.com/pagead/conversion_async.js"></script>
<!-- Google +1 -->
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
</head>
<body>
<a class="badge appstore" href="http://itunes.apple.com/app/id510296984" onclick="goog_report_conversion('index-fixed-header');_gaq.push(['_trackPageview', '/outbound/itunes']);"><img src="img/appstore.png" /></a>
<header>
<div class="badge appstore">
<a href="http://itunes.apple.com/app/id510296984" onclick="goog_report_conversion('index-top-header');_gaq.push(['_trackPageview', '/outbound/itunes']);">
<!--span class="tip appstore">Great feedback may earn you a free copy for a friend!</span-->
<img src="img/appstore.png" />
</a><br />
<form id="sendtophone">
<span class="field">
Or send to your phone:<br />
<input type="text" name="email" placeholder="E-mail or phone number" />
<span class="tip phone">Phone needs country code (eg. +1 for US/CA, +44 for UK)</span>
</span>
<span class="confirm">
Message sent!
</span>
</form>
</div>
<h1><a href="."><img class="logo" src="img/iTunesArtwork-Bare.png" /> Master Password</a></h1>
<div class="footnote"><a href="mailto:masterpassword+remove_this@lyndir.com" onclick="_gaq.push(['_trackPageview', '/outbound/mail']);">Contact</a> | <a href="http://www.lyndir.com" onclick="_gaq.push(['_trackPageview', '/outbound/lyndir.com']);">Lyndir</a> | <a href="https://plus.google.com/116256327773442623984/about" rel="publisher" onclick="_gaq.push(['_trackPageview', '/outbound/google+']);">Google+</a></div>
<div class="divider"></div>
</header>
<div id="fixedheader">
<h2><a href=".">Master Password</a></h2>
</div>
<!--a href="http://bit.ly/vNN5Zi" onclick="_gaq.push(['_trackPageview', '/outbound/testflight']);" id="ribbon"></a-->
<section class="heading">
<div>
<h1>So how does it work? <div class="g-plusone" data-annotation="none" data-href="http://masterpassword.lyndir.com/algorithm.html"></div></h1>
<p>
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.
</p>
<p>
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.
</p>
</div>
</section>
<section>
<h1>The Algorithm</h1>
<p>
Master Password uses a stateless algorithm that relies solely on its implementation and the user's inputs. The user is expected to remember the following information:
<ul>
<li><strong>The master password</strong> (eg. <em>pink fluffy door frame</em>):<br />
This is a secret that the user shares with nobody.</li>
<li><strong>The site name</strong> (eg. <em>apple.com</em>):<br />
The user chooses a name for each site. Its domain name is an ideal choice, since it needn't necessarily be remembered.</li>
<li><strong>The site's password counter</strong> (default: <em>0</em>):<br />
This is an integer that can be incremented when the user needs a new password for the site.</li>
<li><strong>The site's password type</strong> (default: <em>Long Password</em>):<br />
This type determines the format of the output password. It can be changed if the site's password policy does not accept passwords of this format.</li>
</ul>
</p>
<p>
In short, the algorithm is comprised of the following steps:
<ul>
<li>Determining the master <code>key</code></li>
<li>Determining the template <code>seed</code></li>
<li>Encoding a user-friendly <code>password</code></li>
</ul>
</p>
<p>
A note on types:
<ul>
<li>Any character string is UTF-8 de- or encoded, depending on context.</li>
<li>Any number is converted to 32-bit network byte order.</li>
</ul>
</p>
<h2>The Master Password</h2>
<p>
The user chooses a single master password, preferably sufficiently long to harden against brute-force attacks. Master Password recommends absurd three or four-word sentences as they're easily remembered and generally sufficiently high in entropy.
</p>
<p>
The application then creates a <a href="http://www.tarsnap.com/scrypt.html" onclick="_gaq.push(['_trackPageview', '/outbound/tarsnap.com/scrypt.html">scrypt</a> key derivative from the user's password. This process takes quite a bit of processing time and memory. This step exists to make brute-force attempts at guessing the master password from a given output password <strong>far more difficult</strong>, to practically infeasible, even for otherwise vulnerable password strings.
</p>
<code><pre>
key = scrypt( P, S, N, r, p, dkLen )
where
P = master password
S = "com.lyndir.masterpassword" . name length . name
N = 32768
r = 8
p = 2
dkLen = 64
</pre></code>
<p>
The result is a 64-byte <code>key</code> derived from the user's master password. This key will be fed into the rest of the algorithm to produce output passwords that are as private to the user as his master password is.
</p>
<h2>Combining The Inputs</h2>
<p>
The theory behind Master Password requires that all inputs are given by the user. The two main inputs are the master password that we used to determine the <code>key</code> and the site's name. There is a third input value, the password counter, which is a 32-bit unsigned integer value. Initially, the password counter should be zero, but a user may specify a non-zero counter value in case he wants to force the algorithm to produce a new output password for the site.
</p>
<p>
These input values are combined in a byte array, separated by a single <code>NUL</code> byte. In order, the input values are the <code>site name</code>, the master <code>key</code>, and a <code>counter</code>. The byte array is hashed using the HMAC-SHA-256 algorithm to yield the <code>seed</code> as a result.
</p>
<code><pre>
seed = hmac-sha256( key, "com.lyndir.masterpassword" . site name length . site name . counter )
</pre></code>
<h2>Generating The Output</h2>
<p>
We now have a <code>seed</code> which is a sufficiently long seemingly-arbitrary string of bytes that is unique to the site and the user. This string of bytes, however, is not very useful for a user to use as a password. We have two additional problems that need to be solved: The output password must be easy for a user to read and type in using a keyboard or smartphone, but it should also be compatible with most site's password policies.
</p>
<p>
Password policies are strict rules imposed by applications on their users, designed to limit the types of passwords these users are allowed to use with the application. Usually, these policies exist to force users into thinking about passwords with a healthy entropy. Often, they exist purely as a side-effect of bad password handling such as storing the clear-text passwords in a database.
</p>
<p>
Since the idea is that the output password can be used directly as a password to protect the user's account on the site, it needs to be able to pass the site's password policy.
Master Password addresses this problem by introducing <em>password types</em>. Each password type describes what an output password must look like and maps to a set of <code>templates</code>. Templates describe the resulting output password using a series of characters that map to character groups of candidate output characters. A template has the same length as the output password it yields. Each character in the template maps to a specific character group. At each position of the output password, a character is chosen from the character group identified by the character in the template at the same position.
</p>
<p>
The following templates are defined:
<ul>
<li><p>
Type: <strong>Maximum Security Password</strong>
<ul>
<li><code>anoxxxxxxxxxxxxxxxxx</li></code>
<li><code>axxxxxxxxxxxxxxxxxno</li></code>
</ul>
</p></li>
<li><p>Type: <strong>Long Password</strong>
<ul>
<li><code>CvcvnoCvcvCvcv</li></code>
<li><code>CvcvCvcvnoCvcv</li></code>
<li><code>CvcvCvcvCvcvno</li></code>
<li><code>CvccnoCvcvCvcv</li></code>
<li><code>CvccCvcvnoCvcv</li></code>
<li><code>CvccCvcvCvcvno</li></code>
<li><code>CvcvnoCvccCvcv</li></code>
<li><code>CvcvCvccnoCvcv</li></code>
<li><code>CvcvCvccCvcvno</li></code>
<li><code>CvcvnoCvcvCvcc</li></code>
<li><code>CvcvCvcvnoCvcc</li></code>
<li><code>CvcvCvcvCvccno</li></code>
<li><code>CvccnoCvccCvcv</li></code>
<li><code>CvccCvccnoCvcv</li></code>
<li><code>CvccCvccCvcvno</li></code>
<li><code>CvcvnoCvccCvcc</li></code>
<li><code>CvcvCvccnoCvcc</li></code>
<li><code>CvcvCvccCvccno</li></code>
<li><code>CvccnoCvcvCvcc</li></code>
<li><code>CvccCvcvnoCvcc</li></code>
<li><code>CvccCvcvCvccno</li></code>
</ul>
</p></li>
<li><p>Type: <strong>Medium Password</strong>
<ul>
<li><code>CvcnoCvc</code></li>
<li><code>CvcCvcno</code></li>
</ul>
</p></li>
<li><p>Type: <strong>Short Password</strong>
<ul>
<li><code>Cvcn</code></li>
</ul>
</p></li>
<li><p>Type: <strong>Basic Password</strong>
<ul>
<li><code>aaanaaan</code></li>
<li><code>aannaaan</code></li>
<li><code>aaannaaa</code></li>
</ul>
</p></li>
<li><p>Type: <strong>PIN</strong>
<ul>
<li><code>nnnn</code></li>
</ul>
</p></li>
</ul>
</p>
<p>
Where each of the letters above expand any of the characters in their respective character group:
<ul>
<li><p>Template character: <code>V</code>
<ul>
<li><code>AEIOU</code></li>
</ul>
</p></li>
<li><p>Template character: <code>C</code>
<ul>
<li><code>BCDFGHJKLMNPQRSTVWXYZ</code></li>
</ul>
</p></li>
<li><p>Template character: <code>v</code>
<ul>
<li><code>aeiou</code></li>
</ul>
</p></li>
<li><p>Template character: <code>c</code>
<ul>
<li><code>bcdfghjklmnpqrstvwxyz</code></li>
</ul>
</p></li>
<li><p>Template character: <code>A</code> (<code>= V . C</code>)
<ul>
<li><code>AEIOUBCDFGHJKLMNPQRSTVWXYZ</code></li>
</ul>
</p></li>
<li><p>Template character: <code>a</code> (<code>= V . v . C . c</code>)
<ul>
<li><code>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz</code></li>
</ul>
</p></li>
<li><p>Template character: <code>n</code>
<ul>
<li><code>0123456789</code></li>
</ul>
</p></li>
<li><p>Template character: <code>o</code>
<ul>
<li><code>@&amp;%?,=[]_:-+*$#!'^~;()/.</code></li>
</ul>
</p></li>
<li><p>Template character: <code>X</code> (<code>= a . n . o</code>)
<ul>
<li><code>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789@&amp;%?,=[]_:-+*$#!'^~;()/.</code></li>
</ul>
</p></li>
</ul>
</p>
<p>
By default, Master Password uses the <em>Long Password</em> type for any new passwords. The user is able to choose a different password type, which is normally only done if the site's password policy is incompatible with the output password produced by this type.
</p>
<p>
To create the output password, the bytes in the <code>seed</code> are encoded according to the template. The first <code>seed</code> byte is used to determine which of the type's templates to use for encoding an output password. We take the byte value of the first <code>seed</code> byte modulo the amount of templates set for the chosen password type and use the result as a zero-based index in the template list for the password type.
</p>
<code><pre>
templates = [ "CvcvCvcvnoCvcv", "CvcvnoCvcvCvcv", "CvcvCvcvCvcvno", ... ]
template = templates[ seed[0] % count( templates ) ]
</pre></code>
<p>
Now that we know what template to use for building our output password, all that's left is to iterate the template, and produce a character of password output for each step. When we iterate the template (index <code>i</code>), we look in the character group identified by the character (string <code>passChars</code>) in the template at index <code>i</code>.
</p>
<p>
We use the <code>seed</code>'s byte value at index <code>i + 1</code> modulo the amount of characters in the character class to determine which character (<code>passChar</code>) in the class to use for the output password at index <code>i</code>.
</p>
<code><pre>
passChar = passChars[ seed[i + 1] % count( passChars ) ]
passWord[i] = passChar
</pre></code>
<hr />
<a class="next" href="https://github.com/Lyndir/MasterPassword">Show me the code!</a>
</section>
<span itemscope itemtype="http://schema.org/MobileSoftwareApplication">
<meta itemprop="image" content="http://masterpassword.lyndir.com/img/iTunesArtwork-Rounded.png" />
<meta itemprop="name" content="Master Password" />
<meta itemprop="operatingsystems" content="iOS" />
<meta itemprop="softwareversion" content="5.0" />
<span itemprop="offers" itemscope itemtype="http://schema.org/Offer">
<meta itemprop="name" content="iOS" />
<meta itemprop="price" content="5.99" />
<meta itemprop="priceCurrency" content="USD" />
<span itemprop="seller" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Apple" />
<meta itemprop="url" content="http://itunes.apple.com/app/id510296984" />
</span>
</span>
<span itemprop="author" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Lyndir" />
<meta itemprop="url" content="http://www.lyndir.com" />
</span>
</span>
<footer>
Master Password is a security and productivity product by <a href="http://www.lyndir.com" rel="author" onclick="_gaq.push(['_trackPageview', '/outbound/lyndir.com']);">Lyndir</a>, &copy; 2011.
</footer>
</body>
</html>