initial commit for GitHub

This commit is contained in:
Mike Hamburg
2010-05-26 15:34:42 -07:00
commit 2e423d9517
155 changed files with 31749 additions and 0 deletions

BIN
demo/alpha-arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

179
demo/example.css Normal file
View File

@@ -0,0 +1,179 @@
* {
margin: 0px;
padding: 0px;
font-family: Arial, Helvetica, FreeSans, sans;
}
h1 {
text-align: center;
background: #eee;
padding: 5px;
margin-bottom: 0.6em;
font-size: 1.5em;
}
.header {
width: 650px;
margin: 0px auto 1em;
}
p+p {
margin-top: 1em;
}
.explanation {
color: #555;
margin-top: 0.3em;
}
.section+.section, .explanation+.section {
margin-top: 1.5em;
}
.hex {
text-transform: uppercase;
}
.hex, .base64, #ciphertext {
font-family: 'Courier', mono;
}
.wide, textarea {
width: 100%;
margin: 0px -4px;
font-size: inherit;
text-align: left;
}
textarea+*, .wide+* {
margin-top: 0.3em;
}
/* bulk object placement */
#theForm {
position: relative;
width: 940px;
margin: 0px auto;
font-size: 0.8em;
}
.column {
top: 0px;
width: 300px;
}
.box {
border: 2px solid #999;
padding: 7px;
margin-bottom: 20px;
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
}
#cmode { position: absolute; left: 640px; }
#ctexts { position: absolute; left: 320px; }
.floatright {
float: right;
text-align: right;
}
a {
cursor: pointer;
color: #282;
}
a.random, #buttons a { text-decoration: none; }
a.random:hover, a.random:focus { text-decoration: underline; }
h2 {
margin: -7px -7px 3px -7px;
text-align: center;
font-size: 1.2em;
color: white;
background: #999;
}
#pplaintext { border-color: #f65; }
#pplaintext h2 { background: #f65; }
#ppassword { border-color: #4a4; }
#ppassword h2 { background: #4a4; }
#pciphertext { border-color: #78f; }
#pciphertext h2 { background: #78f; }
#buttons { text-align: center; margin-top: -20px; }
a#doPbkdf2, a#encrypt, a#decrypt {
display: inline-block;
text-align: center;
height: 43px;
padding-top: 20px;
width: 50px;
background: url('alpha-arrow.png') no-repeat bottom center;
vertical-align: middle;
border: none;
color: white;
overflow: hidden;
}
.turnDown {
display: inline-block;
padding-bottom: 3px;
-moz-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
background-color: inherit;
}
.turnUp {
display: inline-block;
padding-bottom: 3px;
-moz-transform: rotate(-90deg);
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
background-color: inherit;
}
.buttons a.disabled {
background-color: #ccc ! important;
cursor: inherit ! important;
}
a#encrypt { background-color: #f65; margin-bottom: 2px; }
a#encrypt:hover, a#encrypt:focus { background-color: #f76; }
a#encrypt:active { background-color: #f87; }
a#decrypt {
height: 36px;
padding-top: 27px;
background: url('alpha-arrow.png') no-repeat top center;
background-color: #78f;
margin-top: 2px;
}
a#decrypt:hover { background-color: #89f; }
a#decrypt:focus { background-color: #89f; }
a#decrypt:active { background-color: #9af; }
#ppassword, #pkey, #pmode, #pplaintext, #pciphertext {
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
}
input[type='text'], input[type='password'], textarea {
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
font-size: inherit;
border: 1px solid #444;
padding: 3px;
}
input[type='text']:focus, input[type='password']:focus, textarea:focus {
border-color: red;
}
input[type="radio"], input[type="checkbox"] {
position: relative;
top: 0.15em;
margin-right: -0.15em;
}

153
demo/example.js Normal file
View File

@@ -0,0 +1,153 @@
/* keep track of which salts have been used. */
var form, usedIvs = {'':1}, usedSalts = {'':1};
/* enter actions */
var enterActions = {
password: doPbkdf2,
salt: doPbkdf2,
iter: doPbkdf2
};
function loaded() {
form = new formHandler('theForm', enterActions);
form._extendedKey = [];
sjcl.random.startCollectors();
document.getElementById("password").focus();
}
/* there's probaby a better way to tell the user something, but oh well */
function error(x) {
alert(x);
}
/* compute PBKDF2 on the password. */
function doPbkdf2(decrypting) {
var v = form.get(), salt=v.salt, key, hex = sjcl.codec.hex.fromBits, p={},
password = v.password;
p.iter = v.iter;
if (password.length == 0) {
if (decrypting) { error("Can't decrypt: need a password!"); }
return;
}
if (salt.length === 0 && decrypting) {
error("Can't decrypt: need a salt for PBKDF2!");
return;
}
if (decrypting || !v.freshsalt || !usedSalts[v.salt]) {
p.salt = v.salt;
}
p = sjcl.misc.cachedPbkdf2(password, p);
form._extendedKey = p.key;
v.key = p.key.slice(0, v.keysize/32);
v.salt = p.salt;
form.set(v);
form.plaintext.el.select();
}
/* Encrypt a message */
function doEncrypt() {
var v = form.get(), iv = v.iv, password = v.password, key = v.key, adata = v.adata, aes, plaintext=v.plaintext, rp = {}, ct, p;
if (plaintext === '' && v.ciphertext.length) { return; }
if (key.length == 0 && password.length == 0) {
error("need a password or key!");
return;
}
p = { adata:v.adata,
iter:v.iter,
mode:v.mode,
ts:parseInt(v.tag),
ks:parseInt(v.keysize) };
if (!v.freshiv || !usedIvs[v.iv]) { iv:v.iv; }
if (!v.freshsalt || !usedSalts[v.salt]) { p.salt = v.salt; }
ct = sjcl.encrypt(password || key, plaintext, p, rp).replace(/,/g,",\n");
v.iv = rp.iv;
usedIvs[rp.iv] = 1;
if (rp.salt) {
v.salt = rp.salt;
usedSalts[rp.salt] = 1;
}
v.key = rp.key;
if (v.json) {
v.ciphertext = ct;
v.adata = '';
} else {
v.ciphertext = ct.match(/ct:"([^"]*)"/)[1]; //"
}
v.plaintext = '';
form.set(v);
form.ciphertext.el.select();
}
/* Decrypt a message */
function doDecrypt() {
var v = form.get(), iv = v.iv, key = v.key, adata = v.adata, aes, ciphertext=v.ciphertext, rp = {};
if (ciphertext.length === 0) { return; }
if (!v.password && !v.key.length) {
error("Can't decrypt: need a password or key!"); return;
}
if (ciphertext.match("{")) {
/* it's jsonized */
try {
v.plaintext = sjcl.decrypt(v.password || v.key, ciphertext, {}, rp);
} catch(e) {
error("Can't decrypt: "+e);
return;
}
v.mode = rp.mode;
v.iv = rp.iv;
v.adata = rp.adata;
if (v.password) {
v.salt = rp.salt;
v.iter = rp.iter;
v.keysize = rp.ks;
v.tag = rp.ts;
}
v.key = rp.key;
v.ciphertext = "";
document.getElementById('plaintext').select();
} else {
/* it's raw */
ciphertext = sjcl.codec.base64.toBits(ciphertext);
if (iv.length === 0) {
error("Can't decrypt: need an IV!"); return;
}
if (key.length === 0) {
if (v.password.length) {
doPbkdf2(true);
key = v.key;
}
}
aes = new sjcl.cipher.aes(key);
try {
v.plaintext = sjcl.codec.utf8String.fromBits(sjcl.mode[v.mode].decrypt(aes, ciphertext, iv, v.adata, v.tag));
v.ciphertext = "";
document.getElementById('plaintext').select();
} catch (e) {
error("Can't decrypt: " + e);
}
}
form.set(v);
}
function extendKey(size) {
form.key.set(form._extendedKey.slice(0,size));
}
function randomize(field, words, paranoia) {
form[field].set(sjcl.random.randomWords(words, paranoia));
if (field == 'salt') { form.key.set([]); }
}

137
demo/form.js Normal file
View File

@@ -0,0 +1,137 @@
/* Hackish form handling system. */
function hasClass(e, cl) {
return (" "+e.className+" ").match(" "+cl+" ");
}
function stopPropagation(e) {
e.preventDefault && e.preventDefault();
e.cancelBubble = true;
}
/* proxy for a form object, with appropriate encoder/decoder */
function formElement(el) {
this.el = el;
}
formElement.prototype = {
get: function() {
var el = this.el;
if (el.type == "checkbox") {
return el.checked;
} else if (hasClass(el, "numeric")) {
return parseInt(el.value);
} else if (hasClass(el, "hex")) {
return sjcl.codec.hex.toBits(el.value);
} else if (hasClass(el, "base64")) {
return sjcl.codec.base64.toBits(el.value);
} else {
return el.value;
}
},
set: function(x) {
var el = this.el;
if (el.type == "checkbox") {
el.checked = x; return;
} else if (hasClass(el, "hex")) {
if (typeof x !== 'string') {
x = sjcl.codec.hex.fromBits(x);
}
x = x.toUpperCase().replace(/ /g,'').replace(/(.{8})/g, "$1 ").replace(/ $/, '');
} else if (hasClass(el, "base64")) {
if (typeof x !== 'string') {
x = sjcl.codec.base64.fromBits(x);
}
x = x.replace(/\s/g,'').replace(/(.{32})/g, "$1\n").replace(/\n$/, '');
}
el.value = x;
}
}
function radioGroup(name) {
this.name = name;
}
radioGroup.prototype = {
get: function() {
var els = document.getElementsByName(this.name), i;
for (i=0; i<els.length; i++) {
if (els[i].checked) {
return els[i].value;
}
}
},
set: function(x) {
var els = document.getElementsByName(this.name), i;
for (i=0; i<els.length; i++) {
els[i].checked = (els[i].value == x);
}
}
}
function formHandler(formName, enterActions) {
var i, els = [], tmp, name;
this._elNames = [];
tmp = document.getElementById(formName).getElementsByTagName('input');
for (i=0; i<tmp.length; i++) { els.push(tmp[i]); }
tmp = document.getElementById(formName).getElementsByTagName('textarea');
for (i=0; i<tmp.length; i++) { els.push(tmp[i]); }
for (i=0; i<els.length; i++) {
name = els[i].name || els[i].id;
/* enforce numeric properties of element */
els[i].onkeypress = (function(e) {
return function(ev) {
ev = ev || window.event;
var key = ev.keyCode || ev.which,
keyst = String.fromCharCode(ev.charCode || ev.keyCode),
ente = enterActions[e.name||e.id];
if (ev.ctrlKey || ev.metaKey) {
return;
}
(key == 13) && ente && ente();
if (hasClass(e, 'numeric') && ev.charCode && !keyst.match(/[0-9]/)) {
stopPropagation(ev); return false;
} else if (hasClass(e, 'hex') && ev.charCode && !keyst.match(/[0-9a-fA-F ]/)) {
stopPropagation(ev); return false;
}
}
})(els[i]);
if (els[i].type == 'radio') {
if (this[name] === undefined) {
this[name] = new radioGroup(name);
this._elNames.push(name);
}
} else {
/* code to get the value of an element */
this[name] = new formElement(els[i]);
this._elNames.push(name);
}
}
}
formHandler.prototype = {
get:function() {
var i, out = {}, en = this._elNames;
for (i=0; i<en.length; i++) {
out[en[i]] = this[en[i]].get();
}
return out;
},
set:function(o) {
var i;
for (i in o) {
if (o.hasOwnProperty(i) && this.hasOwnProperty(i)) {
this[i].set(o[i]);
}
}
}
}

208
demo/index.html Normal file
View File

@@ -0,0 +1,208 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SJCL demo</title>
<link rel="stylesheet" type="text/css" href="example.css"/>
<script type="text/javascript" src="../sjcl.js"></script>
<script type="text/javascript" src="form.js"></script>
<script type="text/javascript" src="example.js"></script>
</head>
<body onload="loaded()">
<h1>SJCL demo</h1>
<div class="header">
<p>This page is a demo of the Stanford Javascript Crypto Library. To get started, just type in a password in the left pane and a secret message in the middle pane, then click "encrypt". Encryption takes place in your browser and we never see the plaintext.</p>
<p>SJCL has lots of other options, many of which are shown in the grey boxes.</p>
</div>
<form id="theForm" onsubmit="return false;">
<div class="column" id="ckey">
<!-- Password and pbkdf2 parameters -->
<div class="box" id="ppassword">
<h2>Password</h2>
<div class="section">
<label for="password">Password:</label>
<input type="password" class="wide" name="password" id="password" autocomplete="off" tabindex="1"/>
<p class="explanation">
Choose a strong, random password.
</p>
</div>
</div>
<div class="box" id="pkey">
<h2>Key Derivation</h2>
<div class="section">
<div>
<label for="salt"">Salt:</label>
<a class="random floatright" href="javascript:randomize('salt',2,0)">random</a>
</div>
<input type="text" id="salt" class="wide hex" autocomplete="off" size="17" maxlength="35"/>
<input type="checkbox" name="freshsalt" id="freshsalt" autocomplete="off" checked="checked"/>
<label for="freshsalt">Use fresh random salt for each new password</label>
<p class="explanation">
Salt adds more variability to your key, and prevents attackers
from using <a href="http://en.wikipedia.org/wiki/Rainbow_table">rainbow tables</a> to attack it.
</p>
</div>
<div class="section">
<label for="iter">Strengthen by a factor of:</label>
<input type="text" name="iter" id="iter" value="1000" class="numeric" size="5" maxlength="5" autocomplete="off"/>
<p class="explanation">
Strengthening makes it slower to compute the key corresponding to your
password. This makes it take much longer for an attacker to guess it.
</p>
</div>
<div class="section">
Key size:
<input type="radio" name="keysize" value="128" id="key128" checked="checked" autocomplete="off" onclick="extendKey(4)"/>
<label for="key128">128</label>
<input type="radio" name="keysize" value="192" id="key192" autocomplete="off" onclick="extendKey(6)"/>
<label for="key192">192</label>
<input type="radio" name="keysize" value="256" id="key256" autocomplete="off" onclick="extendKey(8)"/>
<label for="key256">256</label>
<p class="explanation">
128 bits should be secure enough, but you can generate a longer
key if you wish.
</p>
</div>
<!-- cipher key -->
<div class="section">
<div>
<label for="key">Key:</label>
<!--
<a class="random floatright" href="javascript:randomizeKey()">random</a>
-->
</div>
<textarea id="key" name="key" class="hex" rows="2" autocomplete="off"></textarea>
<p class="explanation">
This key is computed from your password, salt and strengthening factor. It
will be used internally by the cipher. Instead of using a password, you can
enter a key here directly. If you do, it should be 32, 48 or 64 hexadecimal
digits (128, 192 or 256 bits).
</p>
</div>
</div>
</div>
<!-- mode controls -->
<div class="column" id="cmode">
<div class="box">
<h2>Cipher Parameters</h2>
<p class="explanation">
SJCL encrypts your data with the <a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard"><acronym title="Advanced Encryption Standard">AES</acronym></a> block cipher.
</p>
<div class="section">
Cipher mode:
<input type="radio" name="mode" value="ccm" id="ccm" checked="checked" autocomplete="off"/>
<label for="ccm"><acronym title="Counter mode with Cipher block chaining Message authentication code">CCM</acronym></label>
<input type="radio" name="mode" value="ocb2" id="ocb2" autocomplete="off"/>
<label for="ocb2"><acronym title="Offset CodeBook mode, version 2.0">OCB2</acronym></label>
<p class="explanation">
The cipher mode is a standard for how to use AES and other
algorithms to encrypt and authenticate your message.
<a href="http://en.wikipedia.org/wiki/OCB_mode">OCB2 mode</a>
is slightly faster and has more features, but
<a href="http://en.wikipedia.org/wiki/CCM_mode">CCM mode</a> has wider
support because it is not patented.
</p>
</div>
<div class="section">
<div>
<label for="iv">Initialization vector:</label>
<a class="random floatright" href="javascript:randomize('iv',4,0)">random</a>
</div>
<input type="text" name="iv" id="iv" class="wide hex" size="32" maxlength="35" autocomplete="off"/>
<input type="checkbox" id="freshiv" autocomplete="off" checked="checked"/>
<label for="freshiv">Choose a new random IV for every message.</label>
<p class="explanation">
The IV needs to be different for every message you send. It adds
randomness to your message, so that the same message will look
different each time you send it.
</p>
<p class="explanation">
Be careful: CCM mode doesn't use
the whole IV, so changing just part of it isn't enough.
</p>
</div>
<div class="section">
Authentication strength:
<input type="radio" name="tag" value="64" id="tag64" autocomplete="off" checked="checked"/>
<label for="tag64">64</label>
<input type="radio" name="tag" value="96" id="tag96" autocomplete="off"/>
<label for="tag96">96</label>
<input type="radio" name="tag" value="128" id="tag128" autocomplete="off"/>
<label for="tag128">128</label>
<p class="explanation">
SJCL adds a an authentication tag to your message to make sure
nobody changes it. The longer the authentication tag, the harder it is
for somebody to change your encrypted message without you noticing. 64
bits is probably enough.
</p>
</div>
<div class="section">
<input type="checkbox" name="json" id="json" autocomplete="off" checked="checked"/>
<label for="json">Send the parameters and authenticated data along
with the message.</label>
<p class="explanation">
These parameters are required to decrypt your message later. If the
person you're sending the message to knows them, you don't need to send
them so your message will be shorter.
</p>
<p class="explanation">
Default parameters won't be sent. Your password won't be sent, either.
The salt and iv will be encoded in base64 instead of hex, so they'll
look different from what's in the box.
</p>
</div>
</div>
</div>
<div class="column" id="ctexts">
<div id="pplaintext" class="box">
<h2>Plaintext</h2>
<div class="section">
<label for="plaintext">Secret message:</label>
<textarea id="plaintext" autocomplete="off" rows="5" tabindex="2"></textarea>
<div class="explanation">
This message will be encrypted, so that nobody can read it or change it
without your password.
</div>
</div>
<div class="section">
<label for="adata">Authenticated data:</label>
<textarea id="adata" autocomplete="off" tabindex="3"></textarea>
<div class="explanation">
This auxilliary message isn't secret, but its integrity will be checked
along with the integrity of the message.
</div>
</div>
</div>
<div id="buttons">
<a href="javascript:doEncrypt()" id="encrypt" tabindex="4"><span class="turnDown">encrypt</span></a>
<a href="javascript:doDecrypt()" id="decrypt" tabindex="6"><span class="turnUp">decrypt</span></a>
</div>
<div id="pciphertext" class="box">
<h2>Ciphertext</h2>
<label for="ciphertext">Ciphertext:</label>
<textarea id="ciphertext" autocomplete="off" rows="7" tabindex="5"></textarea>
<div class="explanation">
Your message, encrypted and authenticated so that nobody can read it
or change it without your password.
</div>
</div>
</form>
</body>
</html>