Created
May 2, 2014 19:04
-
-
Save navinpai/0c8dd8042fc898502b37 to your computer and use it in GitHub Desktop.
Solution of CS255-p1 (https://crypto.stanford.edu/~dabo/courses/cs255_winter13/hw_and_proj/cs255-p1-handout.pdf)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @namespace CS255 | |
// @name CS255 | |
// @description CS255 - Assignment 1 | |
// @version 1.1 | |
// | |
// | |
// @include http://www.facebook.com/* | |
// @include https://www.facebook.com/* | |
// @exclude http://www.facebook.com/messages/* | |
// @exclude https://www.facebook.com/messages/* | |
// @exclude http://www.facebook.com/events/* | |
// @exclude https://www.facebook.com/events/* | |
// ==/UserScript== | |
/* | |
Step 1: change filename, @namespace, @name, and @description above. | |
Please use "CS255-Lastname1-Lastname2" with you and your partners last names | |
to make our grading less painful. | |
Chrome (recommended you use 16.0, use the latest non-beta version): | |
http://support.google.com/chrome/bin/answer.py?hl=en&answer=95346 | |
GreaseMonkey Site: | |
http://www.greasespot.net/ | |
GreaseMonkey Wiki (manual, tutorials, etc..) | |
http://wiki.greasespot.net/Main_Page | |
Javascript reference and tutorials | |
http://www.w3schools.com/js/default.asp | |
Google Chrome Developer Tools | |
http://code.google.com/chrome/devtools/docs/console.html | |
HTML5 Local Storage | |
http://php-html.net/tutorials/html5-local-storage-guide/ | |
And of course... our favorite time waster... Facebook | |
(You'll want a throw-away account) | |
http://www.facebook.com/ | |
Note: | |
For key-management to work you have to enable secure browsing for facebook. | |
Account Setting -> Security -> Secure Browsing | |
*/ | |
var my_username; // user signed in as | |
var page_username; // the owner of the page being viewed | |
var keys = []; // array of keys, each key is a [ user, group, key ]; | |
var padding = '~'; | |
// | |
// Some initialization functions are called at the very end of this script. | |
// After some things are declared it needs. | |
function MAC( msg , key ) | |
{ | |
var msg1 = msg.split(','); | |
var mac = 1598753246; | |
for(var i = 0; i<msg1.length; i++) | |
{ | |
msg1[i]=parseInt(msg1[i]); | |
} | |
for(var i = 0; i<msg1.length; i++) | |
{ | |
mac = mac ^ msg1[i]; | |
} | |
for(var i = 0; i<key.length; i++) | |
{ | |
mac = mac ^ key[i]; | |
} | |
return mac; | |
} | |
function Encrypt( msg , group ) { | |
var e_msg = ""; | |
var i; | |
var e_text; | |
var key1 = new Array(4); | |
var tempkey; | |
var cipher; | |
// CS255-todo: encrypt the msg, using key for the group. | |
if ( ( msg.indexOf( 'rot13:' ) == 0 ) || ( msg.length < 1 ) ) | |
{ | |
// already done, or blank | |
alert( "Try entering a msg (the button works only once)" ); | |
return msg; | |
} | |
else | |
{ | |
// encrypt, add tag. | |
if(msg.length%4 != 0) | |
{ | |
var padding_length = msg.length%4; | |
for ( i = 0 ; i < (4-padding_length) ; ++i) | |
{ | |
msg = msg.concat(padding); | |
} | |
} | |
var len = msg.length; | |
for(i = 0 ; i < keys.length ; i=i+1) | |
{ | |
if(keys[i][0] == group) | |
{ | |
tempkey = keys[i][1]; | |
break; | |
} | |
} | |
var key1=tempkey.split(","); | |
// console.log(key1) | |
for(i=0;i<key1.length;i++) | |
{ | |
key1[i]=parseInt(key1[i]); | |
} | |
cipher = new sjcl.cipher.aes(key1); | |
for(i = 0 ; i < len ; i=i+4) | |
{ | |
var msg_chunk = new Array(4); | |
var temp = msg.substring(i , i+4); | |
for(var x = 0; x < 4 ;x++) | |
msg_chunk[x] = temp.charCodeAt(x); | |
//console.log(msg_chunk); | |
e_text = cipher.encrypt(msg_chunk); | |
e_msg = e_msg +','+ e_text; | |
} | |
e_mac = MAC( e_msg , key1) ; | |
for(i = 0; i < key1.length; i++) | |
{ | |
e_mac = e_mac + key1[i]; | |
} | |
// alert(e_mac); | |
return 'rot13:' +','+ e_mac + rot13( e_msg ); | |
} | |
} | |
function Decrypt( msg , group) { | |
// CS255-todo: implement decryption on encrypted messeges | |
var i; | |
var p_text; | |
var p_msg=""; | |
var msg_chunk; | |
var tempkey; | |
var cipher; | |
// decrypt, ignore the tag. | |
if ( msg.indexOf( '<p>rot13:' ) == 0 ) { | |
msg=msg.substring(10,msg.length-4); | |
msg=msg.split(','); | |
for(i = 0 ; i < keys.length ; i=i+1) | |
{ | |
if(keys[i][0] == group) | |
{ | |
tempkey = keys[i][1]; | |
break; | |
} | |
} | |
var key1=tempkey.split(","); | |
for(i=0;i<key1.length;i++) | |
{ | |
key1[i]=parseInt(key1[i]); | |
} | |
var mac=1598753246; | |
for (i = 0; i < msg.length; i++) | |
{ | |
msg[i] = parseInt(msg[i]); | |
} | |
for(i = 0; i < key1.length; i++) | |
{ | |
msg[0] = msg[0] - key1[i]; | |
} | |
for ( i = 1; i < msg.length; i++ ) | |
{ | |
mac = mac ^ msg[i]; | |
} | |
for ( i = 0; i < key1.length; i++) | |
{ | |
mac = mac ^ key1[i]; | |
} | |
if(mac != msg[0]) | |
{ | |
return "message integrity is lost." | |
} | |
cipher = new sjcl.cipher.aes(key1); | |
/* | |
var msg_chunk=new Array(4); | |
msg_chunk[0]=1837205502; | |
msg_chunk[1]=1916849702; | |
msg_chunk[2]=-1623711125; | |
msg_chunk[3]=-1107706946; | |
a=cipher.decrypt(msg_chunk) | |
for(i=0;i<a.length;i++) | |
alert(String.fromCharCode(a[i])); | |
*/ | |
for(i = 1 ; i < msg.length ; i=i+4) | |
{ | |
var msg_chunk = new Array(4); | |
for(var x = 0; x < 4 ;x++) | |
msg_chunk[x] = msg[i+x]; | |
console.log(msg_chunk); | |
p_text = cipher.decrypt(msg_chunk); | |
var myMsg=""; | |
for(var j=0;j<p_text.length;j++) | |
{ | |
myMsg+=String.fromCharCode(p_text[j]); | |
} | |
console.log(myMsg); | |
p_msg = p_msg + myMsg; | |
} | |
return p_msg; /*+ '<br><font color="red">' | |
+ rot13( p_msg.slice( 6 ) ) + '</font>';*/ | |
} | |
else { | |
return msg; | |
} | |
} | |
function UserName() | |
{ | |
userstring=document.getElementsByClassName('navLink')[0].href; | |
return userstring.substring(userstring.lastIndexOf('/')+1,userstring.lastIndexOf('?')); | |
} | |
function GenerateKey() { | |
group = document.getElementById( 'gen-key-group' ).value; | |
if ( group.length < 1 ) { | |
alert( "You need to set a group" ); | |
return; | |
} | |
// CS255-todo: Well this needs some work... | |
key = GetEntropy(4); | |
// alert( key1 ); | |
// key = 'CS255-todo'; | |
new_key = [group, key ]; | |
keys.push( new_key ); | |
SaveKeys(); | |
UpdateKeysTable(); | |
} | |
function db(groupname,key) | |
{ | |
//console.log("check db"); | |
user_name = UserName(); | |
//console.log(user_name); | |
var db = openDatabase(user_name, '2.0', 'Test_DB', 65535); | |
var msg; | |
db.transaction(function (tx) { | |
console.log("transacting"); | |
tx.executeSql('create table if not exists group_keys("group" varchar(128), "key" varchar(128))'); | |
tx.executeSql('insert into group_keys("group","key") values ("'+groupname+'", "'+key+'")'); | |
console.log(groupname+"groupname"+keys+"key"); | |
//tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")'); | |
}); | |
} | |
function loadKeysFromDB() | |
{ | |
user_name = UserName(); | |
//console.log(user_name); | |
var db = openDatabase(user_name, '2.0', 'Test_DB', 65535); | |
var msg; | |
db.transaction(function (tx) { | |
// console.log("transacting"); | |
tx.executeSql('select * from group_keys',[],function(tx,results){ | |
for (var i=0; i < results.rows.length; i++){ | |
row = results.rows.item(i); | |
keys.push([row.group,row.key]); | |
//editRecords2(row.name,row.total); | |
} | |
}); | |
}); | |
} | |
function ClearTable() | |
{ | |
user_name = UserName(); | |
var db = openDatabase(user_name, '2.0', 'Test_DB', 65535); | |
//console.log("clear table"); | |
db.transaction(function (tx) { | |
//console.log("deleting"); | |
tx.executeSql('delete from group_keys'); | |
}); | |
} | |
function SaveKeys() { | |
//rows = []; | |
ClearTable(); | |
//alert(keys.length); | |
for ( i=0; i<keys.length; i++ ) { | |
// CS255-todo: plaintext keys going to disk? | |
var group = keys[i][0]; | |
var key = keys[i][1]; | |
console.log(group+"%"+key); | |
db(group,key); | |
//rows[i] = keys[i][0] + '$' + keys[i][1]; | |
} | |
//console.log(group+"%"+key); | |
// value = rows.join( '$$' ); | |
//localStorage.setItem('facebook-keys-' + my_username, encodeURIComponent( value ) ); | |
} | |
function LoadKeys() { | |
loadKeysFromDB(); | |
} | |
///////////////////////////////////////////////////////// | |
///////////////////////////////////////////////////////// | |
// | |
// Should not _have_ to change anything below here. | |
// Helper functions and sample code. | |
// | |
///////////////////////////////////////////////////////// | |
///////////////////////////////////////////////////////// | |
function SetupUsernames() { | |
// get who you are logged in as | |
meta = document.getElementsByClassName('topNavLink tinyman')[0]; | |
if(typeof meta !== "undefined") { | |
usernameMatched = /www.facebook.com\/(.*?)ref=tn_tnmn/i.exec(meta.innerHTML); | |
usernameMatched = usernameMatched[1].replace(/&/,''); | |
usernameMatched = usernameMatched.replace(/\?/,''); | |
usernameMatched = usernameMatched.replace(/profile\.phpid=/,''); | |
my_username = usernameMatched; | |
} | |
} | |
function getClassName(obj) { | |
if (typeof obj != "object" || obj === null) return false; | |
return /(\w+)\(/.exec(obj.constructor.toString())[1]; | |
} | |
function hasClass(element, cls) { | |
var r = new RegExp('\\b' + cls + '\\b'); | |
return r.test(element.className); | |
} | |
function DocChanged(e) { | |
if (document.URL.match(/groups/)) { | |
//Check for adding encrypt button for comments | |
if(e.target.nodeType != 3) { | |
decryptTextOfChildNodes(e.target); | |
decryptTextOfChildNodes2(document); | |
if(!hasClass(e.target,"crypto")) { | |
addEncryptCommentButton(e.target); | |
} else { | |
return; | |
} | |
} | |
// Check for adding encrypt button for post | |
if (!document.getElementById('encrypt-button') && !(typeof document.getElementsByClassName('uiList uiListHorizontal clearfix uiComposerBarRightArea rfloat')[0] === "undefined" )) { | |
addEncryptButton(); | |
} | |
} | |
//Check for adding keys-table | |
if (document.URL.match('settings')) { | |
if(!document.getElementById( 'cs255-keys-table' ) && !hasClass(e.target,"crypto")) { | |
AddEncryptionTab(); | |
UpdateKeysTable(); | |
} | |
} | |
} | |
//Decryption of posts | |
function decryptTextOfChildNodes(e) { | |
msgs = e.getElementsByClassName('mbs userContent'); | |
if (msgs.length > 0) { | |
msgs_array = new Array(); | |
for (var i = 0; i < msgs.length; ++i) { | |
msgs_array[i] = msgs[i]; | |
} | |
for (var i = 0; i < msgs_array.length; ++i) { | |
DecryptMsg(msgs_array[i]); | |
} | |
} | |
} | |
//Decryption of comments | |
function decryptTextOfChildNodes2(e) { | |
msgs = e.getElementsByClassName('UFICommentBody'); | |
//console.log(msgs.length+"length"); | |
if (msgs.length > 0) { | |
msgs_array = new Array(); | |
for (var i = 0; i < msgs.length; ++i) { | |
msgs_array[i] = msgs[i].childNodes[0]; | |
} | |
for (var i = 0; i < msgs_array.length; ++i) { | |
DecryptMsg2(msgs_array[i]); | |
} | |
} | |
} | |
function RegisterChangeEvents() { | |
// Facebook loads posts dynamically using AJAX, so we monitor changes | |
// to the HTML to discover new posts or comments. | |
doc = document.addEventListener("DOMNodeInserted", DocChanged, false); | |
} | |
function AddEncryptionTab() { | |
// On the Account Settings page, show the key setups | |
if ( document.URL.match( 'settings' ) ) { | |
div = document.getElementById( 'contentArea' ); | |
if ( div ) { | |
h2 = document.createElement( 'h2' ); | |
h2.setAttribute("class","crypto"); | |
h2.innerHTML = "CS255 Keys"; | |
div.appendChild( h2 ); | |
table = document.createElement( 'table' ); | |
table.id = 'cs255-keys-table'; | |
table.setAttribute( "class","crypto" ); | |
table.setAttribute( 'cellpadding', 3 ); | |
table.setAttribute( 'cellspacing', 1 ); | |
table.setAttribute( 'border', 1 ); | |
table.setAttribute( 'width', "80%" ); | |
div.appendChild( table ); | |
} | |
} | |
} | |
//Encrypt button is added in the upper left corner | |
function addEncryptButton(update) { | |
var encryptLi = document.createElement("li"); | |
encryptLi.setAttribute("class","pls uiListItem uiListHorizontalItemBorder uiListHorizontalItem"); | |
var encryptLabel=document.createElement("label"); | |
encryptLabel.setAttribute("class","submitBtn uiButton uiButtonConfirm"); | |
var encryptButton = document.createElement("input"); | |
encryptButton.setAttribute("value","Encrypt"); | |
encryptButton.setAttribute("type","button"); | |
encryptButton.setAttribute("id","encrypt-button"); | |
encryptButton.setAttribute("class","encrypt-button"); | |
encryptButton.addEventListener( "click", DoEncrypt, false ); | |
encryptLabel.appendChild(encryptButton); | |
encryptLi.appendChild(encryptLabel); | |
var liParent = document.getElementsByClassName('uiList _1dso rfloat _ohf _509- _4ki _6-h _6-j _6-i'); | |
liParent[0].appendChild(encryptLi); | |
decryptTextOfChildNodes(document); | |
decryptTextOfChildNodes2(document); | |
} | |
function addEncryptCommentButton(e) { | |
//There are two possible html divs to which encrypt button can be added. Button is added to one which is present | |
var commentBoxes = e.getElementsByClassName('clearfix UFIMentionsInputWrap'); | |
var newcommentBoxes = e.getElementsByClassName('commentBox'); | |
var addDiv = 0; | |
if(commentBoxes.length) { | |
commentAreas= commentBoxes; | |
} else if (newcommentBoxes.length) { | |
commentAreas= newcommentBoxes; | |
addDiv = 1; | |
} else | |
return; | |
for (j=0; j < commentAreas.length; j++) { | |
var encryptLi = document.createElement("li"); | |
encryptLi.setAttribute("class","pls uiListItem uiListHorizontalItemBorder uiListHorizontalItem crypto"); | |
encryptLi.style.cssFloat = "right"; | |
var encryptLabel=document.createElement("label"); | |
encryptLabel.setAttribute("class","submitBtn uiButton uiButtonConfirm crypto"); | |
var encryptButton = document.createElement("input"); | |
encryptButton.setAttribute("value","Encrypt"); | |
encryptButton.setAttribute("type","button"); | |
encryptButton.setAttribute("id","encrypt-comment-button"); | |
encryptButton.setAttribute("class","encrypt-comment-button crypto"); | |
encryptButton.addEventListener( "click", DoEncrypt, false ); | |
encryptLabel.appendChild(encryptButton); | |
encryptLi.appendChild(encryptLabel); | |
if(addDiv) { | |
var encryptDiv = document.createElement("div"); | |
encryptDiv.setAttribute("class","uiUfiAddTip sendOnEnterTip fss fcg crypto"); | |
encryptDiv.appendChild(encryptLi); | |
commentAreas[j].appendChild(encryptDiv); | |
} else { | |
commentAreas[j].appendChild(encryptLi); | |
} | |
} | |
} | |
function AddElements() { | |
if (document.URL.match(/groups/)) { | |
if (!(typeof document.getElementsByClassName('uiList _1dso rfloat _ohf _509- _4ki _6-h _6-j _6-i')[0] === "undefined" )) { | |
addEncryptButton(); | |
} | |
addEncryptCommentButton(document); | |
} | |
AddEncryptionTab() | |
} | |
function UpdateKeysTable() { | |
table = document.getElementById( 'cs255-keys-table' ); | |
if ( !table ) return; | |
table.innerHTML = ''; | |
console.log("update"+keys); | |
// ugly due to events + GreaseMonkey. | |
// header | |
row = document.createElement( 'tr' ); | |
th = document.createElement( 'th' ); | |
th.innerHTML = "Group"; row.appendChild( th ); | |
th = document.createElement( 'th' ); | |
th.innerHTML = "Key"; row.appendChild( th ); | |
th = document.createElement( 'th' ); | |
th.innerHTML = " "; row.appendChild( th ); | |
table.appendChild( row ); | |
// keys | |
console.log(keys) | |
for ( i = 0 ; i < keys.length ; i++ ) { | |
row = document.createElement( 'tr' ); | |
row.setAttribute("class","" + i); | |
td = document.createElement( 'td' ); | |
td.innerHTML = keys[i][0]; | |
row.appendChild( td ); | |
td = document.createElement( 'td' ); | |
td.innerHTML = keys[i][1]; | |
row.appendChild( td ); | |
td = document.createElement( 'td' ); | |
button = document.createElement( 'input' ); | |
button.type = 'button'; | |
button.value = 'Delete'; | |
button.addEventListener( "click", function(event) { | |
DeleteKey(event.target.parentNode.parentNode); | |
}, false ); | |
td.appendChild( button ); | |
row.appendChild( td ); | |
table.appendChild( row ); | |
} | |
// add friend line | |
row = document.createElement( 'tr' ); | |
td = document.createElement( 'td' ); | |
td.innerHTML = '<input id="new-key-group" type="text" size="8">'; | |
row.appendChild( td ); | |
td = document.createElement( 'td' ); | |
td.innerHTML = '<input id="new-key-key" type="text" size="24">'; | |
row.appendChild( td ); | |
td = document.createElement( 'td' ); | |
button = document.createElement( 'input' ); | |
button.type = 'button'; | |
button.value = 'Add Key'; | |
button.addEventListener( "click", AddKey, false ); | |
td.appendChild( button ); | |
row.appendChild( td ); | |
table.appendChild( row ); | |
// generate line | |
row = document.createElement( 'tr' ); | |
td = document.createElement( 'td' ); | |
td.innerHTML = '<input id="gen-key-group" type="text" size="8">'; | |
row.appendChild( td ); | |
table.appendChild( row ); | |
td = document.createElement( 'td' ); | |
td.colSpan = "2"; | |
button = document.createElement( 'input' ); | |
button.type = 'button'; | |
button.value = 'Generate New Key'; | |
button.addEventListener( "click", GenerateKey, false ); | |
td.appendChild( button ); | |
row.appendChild( td ); | |
} | |
function AddKey() { | |
g = document.getElementById( 'new-key-group' ).value; | |
if ( g.length < 1 ) { | |
alert( "You need to set a group" ); | |
return; | |
} | |
k = document.getElementById( 'new-key-key' ).value; | |
new_key = [ g, k ]; | |
keys.push( new_key ); | |
SaveKeys(); | |
UpdateKeysTable(); | |
} | |
function DeleteKey(e) { | |
keys.splice( e.className , 1 ); | |
SaveKeys(); | |
UpdateKeysTable(); | |
} | |
function DoEncrypt(e) { | |
// triggered by the encrypt button | |
// Contents of post or comment are saved to dummy node. So updation of contens of dummy node is also required after encryption | |
if(e.target.className == "encrypt-button") { | |
textHolder = document.getElementsByClassName( "uiTextareaAutogrow input autofocus mentionsTextarea textInput" )[0]; | |
dummy=document.getElementsByName("xhpc_message")[0]; | |
} | |
else{ | |
textHolder = document.getElementsByClassName( "textInput mentionsTextarea uiTextareaAutogrow uiTextareaNoResize UFIAddCommentInput DOMControl_placeholder" )[0]; | |
dummy=document.getElementsByName("add_comment_text")[0]; | |
//dummy=e.target.parentNode.parentNode.parentNode.parentNode.parentNode.getElementsByClassName("DOMControl_placeholder")[0]; | |
//textHolder = e.target.parentNode.parentNode.parentNode.parentNode.getElementsByClassName( "textInput mentionsTextarea uiTextareaAutogrow uiTextareaNoResize UFIAddCommentInput DOMControl_placeholder" )[0]; | |
} | |
//Get the plain text | |
//var vntext=textHolder.value; | |
var vntext=dummy.value; | |
//Ecrypt | |
var vn2text=Encrypt(vntext,CurrentGroup()); | |
//Replace with encrypted text | |
textHolder.value=vn2text; | |
dummy.value=vn2text; | |
} | |
function CurrentGroup() { | |
// var liParent = document.getElementsByClassName('uiList _1dso rfloat _ohf _509- _4ki _6-h _6-j _6-i'); | |
// var groupName = liParent[0].getElementsByClassName('uiButtonText')[0].innerText; | |
var groupName= document.getElementsByClassName('groupsJumpTitle')[0].innerHTML; | |
// alert(groupName); | |
return groupName; | |
} | |
function GetMsgText( msg ) { | |
return msg.innerHTML; | |
} | |
function getTextFromChildren(parent, skipClass, results) { | |
var children = parent.childNodes, item; | |
var re = new RegExp("\\b" + skipClass + "\\b"); | |
for (var i = 0, len = children.length; i < len; i++) { | |
item = children[i]; | |
// if text node, collect it's text | |
if (item.nodeType == 3) { | |
results.push(item.nodeValue); | |
} else if (!item.className || !item.className.match(re)) { | |
// if it has a className and it doesn't match | |
// what we're skipping, then recurse on it | |
getTextFromChildren(item, skipClass, results); | |
} | |
} | |
} | |
function GetMsgTextForDecryption( msg ) { | |
try { | |
return msg.innerHTML; | |
visibleDiv = msg.getElementsByClassName("text_exposed_root"); | |
if(visibleDiv.length) { | |
var visibleDiv = document.getElementsByClassName("text_exposed_root"); | |
var text = []; | |
getTextFromChildren(visibleDiv[0], "text_exposed_hide", text); | |
mg= text.join(""); | |
return mg; | |
} else { | |
return msg.innerText; | |
} | |
} | |
catch (err) { | |
return msg.innerText; | |
} | |
} | |
function wbr(str, num) { | |
//return str.replace(RegExp("(\\w{" + num + "})(\\w)", "g"), function(all,text,char){ | |
// return text + "<wbr>" + char; | |
//}); | |
return str.replace(RegExp("(.{" + num + "})(.)", "g"), function(all,text,char){ | |
return text + "<wbr>" + char; | |
}); | |
} | |
function SetMsgText( msg, new_text ) { | |
//msg.innerHTML = wbr(new_text, 50); | |
while(new_text.charAt( new_text.length-1 ) == "~") { | |
new_text = new_text.slice(0, -1) | |
} | |
msg.innerHTML = new_text; | |
} | |
function SetMsgText2( msg, new_text ) { | |
while(new_text.charAt( new_text.length-1 ) == "~") { | |
new_text = new_text.slice(0, -1) | |
} | |
//msg.innerHTML = wbr(new_text, 50); | |
msg.innerHTML = new_text; | |
} | |
function DecryptMsg(msg) { | |
// we mark the box with the class "decrypted" to prevent attempting to decrypt it multiple times. | |
if (!/decrypted/.test(msg.className)) { | |
txt = GetMsgTextForDecryption( msg ); | |
auth = CurrentGroup(); | |
SetMsgText( msg, Decrypt( txt , CurrentGroup() ) ); | |
msg.className += " decrypted"; | |
} | |
} | |
function DecryptMsg2(msg) { | |
// we mark the box with the class "decrypted" to prevent attempting to decrypt it multiple times. | |
if (!/decrypted/.test(msg.className)) { | |
txt = GetMsgTextForDecryption( msg ); | |
auth = CurrentGroup(); | |
SetMsgText2( msg, Decrypt('<p>'+txt+'</p>' , CurrentGroup() ) ); | |
msg.className += " decrypted"; | |
} | |
} | |
function CryptoInit() { | |
sjcl.random.startCollectors(); | |
} | |
// Get n words of entropy as an array. Defaults to 1 word | |
function GetEntropy(n) { | |
try { | |
return sjcl.random.randomWords( n || 1, 6 ); | |
} catch (error) { | |
if (error instanceof sjcl.exception.notready) { | |
alert( "Not enough entropy. After clicking OK, move your mouse around for a few seconds before trying again." ); | |
return false; | |
} else { | |
throw error; | |
} | |
} | |
} | |
// Converts given string and salt to "random" bits appropriate | |
// for use in encryption. How to appropriately use the bits is up to you. | |
// | |
// IMPORTANT NOTE: This is *not* the way you want to do this for proper security. | |
// The right way to do it will be covered just before milestone 1 is due. | |
// Kudos to you if you want to fix this function once you learn the right way. | |
// | |
// @param s {String} String to encode | |
// @param salt {String} A salt string | |
// @param len {Number} length of output in number of bits | |
// @param count {Number} Number of times to iterate (default 1000) | |
// @return {bitarray} a bit string | |
function StringToBits(s, salt, len, count) { | |
count = count || 1000; | |
out = s + salt; | |
for (var i = 0; i < count; ++i) { | |
// The first time through we will turn the String into a bitarray | |
out = sjcl.hash.sha256.hash(out); | |
} | |
if (len) { | |
out = sjcl.bitArray.clamp(out, len); | |
} | |
return out; | |
} | |
// From http://aymanh.com/9-javascript-tips-you-may-not-know#Assertion | |
// Just in case you want an assert() function | |
function AssertException(message) { this.message = message; } | |
AssertException.prototype.toString = function () { | |
return 'AssertException: ' + this.message; | |
} | |
function assert(exp, message) { | |
if (!exp) { | |
throw new AssertException(message); | |
} | |
} | |
function rot13( text ) { | |
// JS rot13 from http://jsfromhell.com/string/rot13 | |
return text.replace(/[a-zA-Z]/g, | |
function(c) { | |
return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26); | |
} ); | |
} | |
///////////////////////////////////////////////////////// | |
///////////////////////////////////////////////////////// | |
// | |
// Below here is from other libraries. Here be dragons. | |
// | |
///////////////////////////////////////////////////////// | |
///////////////////////////////////////////////////////// | |
/* | |
Here are the basic cryptographic functions you need to do the assignment: | |
function sjcl.cipher.aes(key) | |
This function creates a new AES encryptor/decryptor with a given key. | |
Note that the key must be an array of 4, 6, or 8 32-bit words for the | |
function to work. For those of you keeping score, this constructor does | |
all the scheduling needed for the cipher to work. | |
encrypt: function(plaintext) | |
This function encrypts the given plaintext (duh). The plaintext argument | |
should take the form of an array of four (32-bit) integers, so the plaintext | |
should only be one block of data. | |
decrypt: function(ciphertext) | |
This function decrypts the given ciphertext. Again, the ciphertext argument | |
should be an array of 4 integers. | |
A silly example of this in action: | |
var key1 = new Array(8); | |
var cipher = new sjcl.cipher.aes(key1); | |
var dumbtext = new Array(4); | |
dumbtext[0] = 1; dumbtext[1] = 2; dumbtext[2] = 3; dumbtext[3] = 4; | |
var ctext = cipher.encrypt(dumbtext); | |
var outtext = cipher.decrypt(ctext); | |
Obviously our key is just all zeroes in this case, but this should illustrate | |
the point. | |
*/ | |
/** @fileOverview Javascript cryptography implementation. | |
* | |
* Crush to remove comments, shorten variable names and | |
* generally reduce transmission size. | |
* | |
* @author Emily Stark | |
* @author Mike Hamburg | |
* @author Dan Boneh | |
*/ | |
"use strict"; | |
/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */ | |
/*global document, window, escape, unescape */ | |
/** @namespace The Stanford Javascript Crypto Library, top-level namespace. */ | |
var sjcl = { | |
/** @namespace Symmetric ciphers. */ | |
cipher: {}, | |
/** @namespace Hash functions. Right now only SHA256 is implemented. */ | |
hash: {}, | |
/** @namespace Block cipher modes of operation. */ | |
mode: {}, | |
/** @namespace Miscellaneous. HMAC and PBKDF2. */ | |
misc: {}, | |
/** | |
* @namespace Bit array encoders and decoders. | |
* | |
* @description | |
* The members of this namespace are functions which translate between | |
* SJCL's bitArrays and other objects (usually strings). Because it | |
* isn't always clear which direction is encoding and which is decoding, | |
* the method names are "fromBits" and "toBits". | |
*/ | |
codec: {}, | |
/** @namespace Exceptions. */ | |
exception: { | |
/** @class Ciphertext is corrupt. */ | |
corrupt: function(message) { | |
this.toString = function() { return "CORRUPT: "+this.message; }; | |
this.message = message; | |
}, | |
/** @class Invalid parameter. */ | |
invalid: function(message) { | |
this.toString = function() { return "INVALID: "+this.message; }; | |
this.message = message; | |
}, | |
/** @class Bug or missing feature in SJCL. */ | |
bug: function(message) { | |
this.toString = function() { return "BUG: "+this.message; }; | |
this.message = message; | |
}, | |
// Added by mbarrien to fix an SJCL bug. | |
/** @class Not ready to encrypt. */ | |
notready: function(message) { | |
this.toString = function() { return "NOTREADY: "+this.message; }; | |
this.message = message; | |
} | |
} | |
}; | |
/** @fileOverview Low-level AES implementation. | |
* | |
* This file contains a low-level implementation of AES, optimized for | |
* size and for efficiency on several browsers. It is based on | |
* OpenSSL's aes_core.c, a public-domain implementation by Vincent | |
* Rijmen, Antoon Bosselaers and Paulo Barreto. | |
* | |
* An older version of this implementation is available in the public | |
* domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh, | |
* Stanford University 2008-2010 and BSD-licensed for liability | |
* reasons. | |
* | |
* @author Emily Stark | |
* @author Mike Hamburg | |
* @author Dan Boneh | |
*/ | |
/** | |
* Schedule out an AES key for both encryption and decryption. This | |
* is a low-level class. Use a cipher mode to do bulk encryption. | |
* | |
* @constructor | |
* @param {Array} key The key as an array of 4, 6 or 8 words. | |
* | |
* @class Advanced Encryption Standard (low-level interface) | |
*/ | |
sjcl.cipher.aes = function (key) { | |
if (!this._tables[0][0][0]) { | |
this._precompute(); | |
} | |
var i, j, tmp, | |
encKey, decKey, | |
sbox = this._tables[0][4], decTable = this._tables[1], | |
keyLen = key.length, rcon = 1; | |
if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) { | |
throw new sjcl.exception.invalid("invalid aes key size"); | |
} | |
this._key = [encKey = key.slice(0), decKey = []]; | |
// schedule encryption keys | |
for (i = keyLen; i < 4 * keyLen + 28; i++) { | |
tmp = encKey[i-1]; | |
// apply sbox | |
if (i%keyLen === 0 || (keyLen === 8 && i%keyLen === 4)) { | |
tmp = sbox[tmp>>>24]<<24 ^ sbox[tmp>>16&255]<<16 ^ sbox[tmp>>8&255]<<8 ^ sbox[tmp&255]; | |
// shift rows and add rcon | |
if (i%keyLen === 0) { | |
tmp = tmp<<8 ^ tmp>>>24 ^ rcon<<24; | |
rcon = rcon<<1 ^ (rcon>>7)*283; | |
} | |
} | |
encKey[i] = encKey[i-keyLen] ^ tmp; | |
} | |
// schedule decryption keys | |
for (j = 0; i; j++, i--) { | |
tmp = encKey[j&3 ? i : i - 4]; | |
if (i<=4 || j<4) { | |
decKey[j] = tmp; | |
} else { | |
decKey[j] = decTable[0][sbox[tmp>>>24 ]] ^ | |
decTable[1][sbox[tmp>>16 & 255]] ^ | |
decTable[2][sbox[tmp>>8 & 255]] ^ | |
decTable[3][sbox[tmp & 255]]; | |
} | |
} | |
}; | |
sjcl.cipher.aes.prototype = { | |
// public | |
/* Something like this might appear here eventually | |
name: "AES", | |
blockSize: 4, | |
keySizes: [4,6,8], | |
*/ | |
/** | |
* Encrypt an array of 4 big-endian words. | |
* @param {Array} data The plaintext. | |
* @return {Array} The ciphertext. | |
*/ | |
encrypt:function (data) { | |
return this._crypt(data,0); }, | |
/** | |
* Decrypt an array of 4 big-endian words. | |
* @param {Array} data The ciphertext. | |
* @return {Array} The plaintext. | |
*/ | |
decrypt:function (data) { return this._crypt(data,1); }, | |
/** | |
* The expanded S-box and inverse S-box tables. These will be computed | |
* on the client so that we don't have to send them down the wire. | |
* | |
* There are two tables, _tables[0] is for encryption and | |
* _tables[1] is for decryption. | |
* | |
* The first 4 sub-tables are the expanded S-box with MixColumns. The | |
* last (_tables[01][4]) is the S-box itself. | |
* | |
* @private | |
*/ | |
_tables: [[[],[],[],[],[]],[[],[],[],[],[]]], | |
/** | |
* Expand the S-box tables. | |
* | |
* @private | |
*/ | |
_precompute: function () { | |
var encTable = this._tables[0], decTable = this._tables[1], | |
sbox = encTable[4], sboxInv = decTable[4], | |
i, x, xInv, d=[], th=[], x2, x4, x8, s, tEnc, tDec; | |
// Compute double and third tables | |
for (i = 0; i < 256; i++) { | |
th[( d[i] = i<<1 ^ (i>>7)*283 )^i]=i; | |
} | |
for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) { | |
// Compute sbox | |
s = xInv ^ xInv<<1 ^ xInv<<2 ^ xInv<<3 ^ xInv<<4; | |
s = s>>8 ^ s&255 ^ 99; | |
sbox[x] = s; | |
sboxInv[s] = x; | |
// Compute MixColumns | |
x8 = d[x4 = d[x2 = d[x]]]; | |
tDec = x8*0x1010101 ^ x4*0x10001 ^ x2*0x101 ^ x*0x1010100; | |
tEnc = d[s]*0x101 ^ s*0x1010100; | |
for (i = 0; i < 4; i++) { | |
encTable[i][x] = tEnc = tEnc<<24 ^ tEnc>>>8; | |
decTable[i][s] = tDec = tDec<<24 ^ tDec>>>8; | |
} | |
} | |
// Compactify. Considerable speedup on Firefox. | |
for (i = 0; i < 5; i++) { | |
encTable[i] = encTable[i].slice(0); | |
decTable[i] = decTable[i].slice(0); | |
} | |
}, | |
/** | |
* Encryption and decryption core. | |
* @param {Array} input Four words to be encrypted or decrypted. | |
* @param dir The direction, 0 for encrypt and 1 for decrypt. | |
* @return {Array} The four encrypted or decrypted words. | |
* @private | |
*/ | |
_crypt:function (input, dir) { | |
// alert("qwe"); | |
if (input.length !== 4) { | |
throw new sjcl.exception.invalid("invalid aes block size"); | |
} | |
var key = this._key[dir], | |
// state variables a,b,c,d are loaded with pre-whitened data | |
a = input[0] ^ key[0], | |
b = input[dir ? 3 : 1] ^ key[1], | |
c = input[2] ^ key[2], | |
d = input[dir ? 1 : 3] ^ key[3], | |
a2, b2, c2, | |
nInnerRounds = key.length/4 - 2, | |
i, | |
kIndex = 4, | |
out = [0,0,0,0], | |
table = this._tables[dir], | |
// load up the tables | |
t0 = table[0], | |
t1 = table[1], | |
t2 = table[2], | |
t3 = table[3], | |
sbox = table[4]; | |
// Inner rounds. Cribbed from OpenSSL. | |
for (i = 0; i < nInnerRounds; i++) { | |
a2 = t0[a>>>24] ^ t1[b>>16 & 255] ^ t2[c>>8 & 255] ^ t3[d & 255] ^ key[kIndex]; | |
b2 = t0[b>>>24] ^ t1[c>>16 & 255] ^ t2[d>>8 & 255] ^ t3[a & 255] ^ key[kIndex + 1]; | |
c2 = t0[c>>>24] ^ t1[d>>16 & 255] ^ t2[a>>8 & 255] ^ t3[b & 255] ^ key[kIndex + 2]; | |
d = t0[d>>>24] ^ t1[a>>16 & 255] ^ t2[b>>8 & 255] ^ t3[c & 255] ^ key[kIndex + 3]; | |
kIndex += 4; | |
a=a2; b=b2; c=c2; | |
} | |
// Last round. | |
for (i = 0; i < 4; i++) { | |
out[dir ? 3&-i : i] = | |
sbox[a>>>24 ]<<24 ^ | |
sbox[b>>16 & 255]<<16 ^ | |
sbox[c>>8 & 255]<<8 ^ | |
sbox[d & 255] ^ | |
key[kIndex++]; | |
a2=a; a=b; b=c; c=d; d=a2; | |
} | |
return out; | |
} | |
}; | |
/** @fileOverview Arrays of bits, encoded as arrays of Numbers. | |
* | |
* @author Emily Stark | |
* @author Mike Hamburg | |
* @author Dan Boneh | |
*/ | |
/** @namespace Arrays of bits, encoded as arrays of Numbers. | |
* | |
* @description | |
* <p> | |
* These objects are the currency accepted by SJCL's crypto functions. | |
* </p> | |
* | |
* <p> | |
* Most of our crypto primitives operate on arrays of 4-byte words internally, | |
* but many of them can take arguments that are not a multiple of 4 bytes. | |
* This library encodes arrays of bits (whose size need not be a multiple of 8 | |
* bits) as arrays of 32-bit words. The bits are packed, big-endian, into an | |
* array of words, 32 bits at a time. Since the words are double-precision | |
* floating point numbers, they fit some extra data. We use this (in a private, | |
* possibly-changing manner) to encode the number of bits actually present | |
* in the last word of the array. | |
* </p> | |
* | |
* <p> | |
* Because bitwise ops clear this out-of-band data, these arrays can be passed | |
* to ciphers like AES which want arrays of words. | |
* </p> | |
*/ | |
sjcl.bitArray = { | |
/** | |
* Array slices in units of bits. | |
* @param {bitArray a} The array to slice. | |
* @param {Number} bstart The offset to the start of the slice, in bits. | |
* @param {Number} bend The offset to the end of the slice, in bits. If this is undefined, | |
* slice until the end of the array. | |
* @return {bitArray} The requested slice. | |
*/ | |
bitSlice: function (a, bstart, bend) { | |
a = sjcl.bitArray._shiftRight(a.slice(bstart/32), 32 - (bstart & 31)).slice(1); | |
return (bend === undefined) ? a : sjcl.bitArray.clamp(a, bend-bstart); | |
}, | |
/** | |
* Concatenate two bit arrays. | |
* @param {bitArray} a1 The first array. | |
* @param {bitArray} a2 The second array. | |
* @return {bitArray} The concatenation of a1 and a2. | |
*/ | |
concat: function (a1, a2) { | |
if (a1.length === 0 || a2.length === 0) { | |
return a1.concat(a2); | |
} | |
var out, i, last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last); | |
if (shift === 32) { | |
return a1.concat(a2); | |
} else { | |
return sjcl.bitArray._shiftRight(a2, shift, last|0, a1.slice(0,a1.length-1)); | |
} | |
}, | |
/** | |
* Find the length of an array of bits. | |
* @param {bitArray} a The array. | |
* @return {Number} The length of a, in bits. | |
*/ | |
bitLength: function (a) { | |
var l = a.length, x; | |
if (l === 0) { return 0; } | |
x = a[l - 1]; | |
return (l-1) * 32 + sjcl.bitArray.getPartial(x); | |
}, | |
/** | |
* Truncate an array. | |
* @param {bitArray} a The array. | |
* @param {Number} len The length to truncate to, in bits. | |
* @return {bitArray} A new array, truncated to len bits. | |
*/ | |
clamp: function (a, len) { | |
if (a.length * 32 < len) { return a; } | |
a = a.slice(0, Math.ceil(len / 32)); | |
var l = a.length; | |
len = len & 31; | |
if (l > 0 && len) { | |
a[l-1] = sjcl.bitArray.partial(len, a[l-1] & 0x80000000 >> (len-1), 1); | |
} | |
return a; | |
}, | |
/** | |
* Make a partial word for a bit array. | |
* @param {Number} len The number of bits in the word. | |
* @param {Number} x The bits. | |
* @param {Number} [0] _end Pass 1 if x has already been shifted to the high side. | |
* @return {Number} The partial word. | |
*/ | |
partial: function (len, x, _end) { | |
if (len === 32) { return x; } | |
return (_end ? x|0 : x << (32-len)) + len * 0x10000000000; | |
}, | |
/** | |
* Get the number of bits used by a partial word. | |
* @param {Number} x The partial word. | |
* @return {Number} The number of bits used by the partial word. | |
*/ | |
getPartial: function (x) { | |
return Math.round(x/0x10000000000) || 32; | |
}, | |
/** | |
* Compare two arrays for equality in a predictable amount of time. | |
* @param {bitArray} a The first array. | |
* @param {bitArray} b The second array. | |
* @return {boolean} true if a == b; false otherwise. | |
*/ | |
equal: function (a, b) { | |
if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) { | |
return false; | |
} | |
var x = 0, i; | |
for (i=0; i<a.length; i++) { | |
x |= a[i]^b[i]; | |
} | |
return (x === 0); | |
}, | |
/** Shift an array right. | |
* @param {bitArray} a The array to shift. | |
* @param {Number} shift The number of bits to shift. | |
* @param {Number} [carry=0] A byte to carry in | |
* @param {bitArray} [out=[]] An array to prepend to the output. | |
* @private | |
*/ | |
_shiftRight: function (a, shift, carry, out) { | |
var i, last2=0, shift2; | |
if (out === undefined) { out = []; } | |
for (; shift >= 32; shift -= 32) { | |
out.push(carry); | |
carry = 0; | |
} | |
if (shift === 0) { | |
return out.concat(a); | |
} | |
for (i=0; i<a.length; i++) { | |
out.push(carry | a[i]>>>shift); | |
carry = a[i] << (32-shift); | |
} | |
last2 = a.length ? a[a.length-1] : 0; | |
shift2 = sjcl.bitArray.getPartial(last2); | |
out.push(sjcl.bitArray.partial(shift+shift2 & 31, (shift + shift2 > 32) ? carry : out.pop(),1)); | |
return out; | |
}, | |
/** xor a block of 4 words together. | |
* @private | |
*/ | |
_xor4: function(x,y) { | |
return [x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]]; | |
} | |
}; | |
/** @fileOverview Javascript SHA-256 implementation. | |
* | |
* An older version of this implementation is available in the public | |
* domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh, | |
* Stanford University 2008-2010 and BSD-licensed for liability | |
* reasons. | |
* | |
* Special thanks to Aldo Cortesi for pointing out several bugs in | |
* this code. | |
* | |
* @author Emily Stark | |
* @author Mike Hamburg | |
* @author Dan Boneh | |
*/ | |
/** | |
* Context for a SHA-256 operation in progress. | |
* @constructor | |
* @class Secure Hash Algorithm, 256 bits. | |
*/ | |
sjcl.hash.sha256 = function (hash) { | |
if (!this._key[0]) { this._precompute(); } | |
if (hash) { | |
this._h = hash._h.slice(0); | |
this._buffer = hash._buffer.slice(0); | |
this._length = hash._length; | |
} else { | |
this.reset(); | |
} | |
}; | |
/** @fileOverview Bit array codec implementations. | |
* | |
* @author Emily Stark | |
* @author Mike Hamburg | |
* @author Dan Boneh | |
*/ | |
/** @namespace UTF-8 strings */ | |
sjcl.codec.utf8String = { | |
/** Convert from a bitArray to a UTF-8 string. */ | |
fromBits: function (arr) { | |
var out = "", bl = sjcl.bitArray.bitLength(arr), i, tmp; | |
for (i=0; i<bl/8; i++) { | |
if ((i&3) === 0) { | |
tmp = arr[i/4]; | |
} | |
out += String.fromCharCode(tmp >>> 24); | |
tmp <<= 8; | |
} | |
return decodeURIComponent(escape(out)); | |
}, | |
/** Convert from a UTF-8 string to a bitArray. */ | |
toBits: function (str) { | |
str = unescape(encodeURIComponent(str)); | |
var out = [], i, tmp=0; | |
for (i=0; i<str.length; i++) { | |
tmp = tmp << 8 | str.charCodeAt(i); | |
if ((i&3) === 3) { | |
out.push(tmp); | |
tmp = 0; | |
} | |
} | |
if (i&3) { | |
out.push(sjcl.bitArray.partial(8*(i&3), tmp)); | |
} | |
return out; | |
} | |
}; | |
/** | |
* Hash a string or an array of words. | |
* @static | |
* @param {bitArray|String} data the data to hash. | |
* @return {bitArray} The hash value, an array of 16 big-endian words. | |
*/ | |
sjcl.hash.sha256.hash = function (data) { | |
return (new sjcl.hash.sha256()).update(data).finalize(); | |
}; | |
sjcl.hash.sha256.prototype = { | |
/** | |
* The hash's block size, in bits. | |
* @constant | |
*/ | |
blockSize: 512, | |
/** | |
* Reset the hash state. | |
* @return this | |
*/ | |
reset:function () { | |
this._h = this._init.slice(0); | |
this._buffer = []; | |
this._length = 0; | |
return this; | |
}, | |
/** | |
* Input several words to the hash. | |
* @param {bitArray|String} data the data to hash. | |
* @return this | |
*/ | |
update: function (data) { | |
if (typeof data === "string") { | |
data = sjcl.codec.utf8String.toBits(data); | |
} | |
var i, b = this._buffer = sjcl.bitArray.concat(this._buffer, data), | |
ol = this._length, | |
nl = this._length = ol + sjcl.bitArray.bitLength(data); | |
for (i = 512+ol & -512; i <= nl; i+= 512) { | |
this._block(b.splice(0,16)); | |
} | |
return this; | |
}, | |
/** | |
* Complete hashing and output the hash value. | |
* @return {bitArray} The hash value, an array of 16 big-endian words. | |
*/ | |
finalize:function () { | |
var i, b = this._buffer, h = this._h; | |
// Round out and push the buffer | |
b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1,1)]); | |
// Round out the buffer to a multiple of 16 words, less the 2 length words. | |
for (i = b.length + 2; i & 15; i++) { | |
b.push(0); | |
} | |
// append the length | |
b.push(Math.floor(this._length / 0x100000000)); | |
b.push(this._length | 0); | |
while (b.length) { | |
this._block(b.splice(0,16)); | |
} | |
this.reset(); | |
return h; | |
}, | |
/** | |
* The SHA-256 initialization vector, to be precomputed. | |
* @private | |
*/ | |
_init:[], | |
/* | |
_init:[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19], | |
*/ | |
/** | |
* The SHA-256 hash key, to be precomputed. | |
* @private | |
*/ | |
_key:[], | |
/* | |
_key: | |
[0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, | |
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, | |
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, | |
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, | |
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | |
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2], | |
*/ | |
/** | |
* Function to precompute _init and _key. | |
* @private | |
*/ | |
_precompute: function () { | |
var i = 0, prime = 2, factor; | |
function frac(x) { return (x-Math.floor(x)) * 0x100000000 | 0; } | |
outer: for (; i<64; prime++) { | |
for (factor=2; factor*factor <= prime; factor++) { | |
if (prime % factor === 0) { | |
// not a prime | |
continue outer; | |
} | |
} | |
if (i<8) { | |
this._init[i] = frac(Math.pow(prime, 1/2)); | |
} | |
this._key[i] = frac(Math.pow(prime, 1/3)); | |
i++; | |
} | |
}, | |
/** | |
* Perform one cycle of SHA-256. | |
* @param {bitArray} words one block of words. | |
* @private | |
*/ | |
_block:function (words) { | |
var i, tmp, a, b, | |
w = words.slice(0), | |
h = this._h, | |
k = this._key, | |
h0 = h[0], h1 = h[1], h2 = h[2], h3 = h[3], | |
h4 = h[4], h5 = h[5], h6 = h[6], h7 = h[7]; | |
/* Rationale for placement of |0 : | |
* If a value can overflow is original 32 bits by a factor of more than a few | |
* million (2^23 ish), there is a possibility that it might overflow the | |
* 53-bit mantissa and lose precision. | |
* | |
* To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that | |
* propagates around the loop, and on the hash state h[]. I don't believe | |
* that the clamps on h4 and on h0 are strictly necessary, but it's close | |
* (for h4 anyway), and better safe than sorry. | |
* | |
* The clamps on h[] are necessary for the output to be correct even in the | |
* common case and for short inputs. | |
*/ | |
for (i=0; i<64; i++) { | |
// load up the input word for this round | |
if (i<16) { | |
tmp = w[i]; | |
} else { | |
a = w[(i+1 ) & 15]; | |
b = w[(i+14) & 15]; | |
tmp = w[i&15] = ((a>>>7 ^ a>>>18 ^ a>>>3 ^ a<<25 ^ a<<14) + | |
(b>>>17 ^ b>>>19 ^ b>>>10 ^ b<<15 ^ b<<13) + | |
w[i&15] + w[(i+9) & 15]) | 0; | |
} | |
tmp = (tmp + h7 + (h4>>>6 ^ h4>>>11 ^ h4>>>25 ^ h4<<26 ^ h4<<21 ^ h4<<7) + (h6 ^ h4&(h5^h6)) + k[i]); // | 0; | |
// shift register | |
h7 = h6; h6 = h5; h5 = h4; | |
h4 = h3 + tmp | 0; | |
h3 = h2; h2 = h1; h1 = h0; | |
h0 = (tmp + ((h1&h2) ^ (h3&(h1^h2))) + (h1>>>2 ^ h1>>>13 ^ h1>>>22 ^ h1<<30 ^ h1<<19 ^ h1<<10)) | 0; | |
} | |
h[0] = h[0]+h0 | 0; | |
h[1] = h[1]+h1 | 0; | |
h[2] = h[2]+h2 | 0; | |
h[3] = h[3]+h3 | 0; | |
h[4] = h[4]+h4 | 0; | |
h[5] = h[5]+h5 | 0; | |
h[6] = h[6]+h6 | 0; | |
h[7] = h[7]+h7 | 0; | |
} | |
}; | |
///////// TROGDOR!!!! (We told you here'd be dragons!) | |
/** @fileOverview Random number generator. | |
* | |
* @author Emily Stark | |
* @author Mike Hamburg | |
* @author Dan Boneh | |
*/ | |
/** @namespace Random number generator | |
* | |
* @description | |
* <p> | |
* This random number generator is a derivative of Ferguson and Schneier's | |
* generator Fortuna. It collects entropy from various events into several | |
* pools, implemented by streaming SHA-256 instances. It differs from | |
* ordinary Fortuna in a few ways, though. | |
* </p> | |
* | |
* <p> | |
* Most importantly, it has an entropy estimator. This is present because | |
* there is a strong conflict here between making the generator available | |
* as soon as possible, and making sure that it doesn't "run on empty". | |
* In Fortuna, there is a saved state file, and the system is likely to have | |
* time to warm up. | |
* </p> | |
* | |
* <p> | |
* Second, because users are unlikely to stay on the page for very long, | |
* and to speed startup time, the number of pools increases logarithmically: | |
* a new pool is created when the previous one is actually used for a reseed. | |
* This gives the same asymptotic guarantees as Fortuna, but gives more | |
* entropy to early reseeds. | |
* </p> | |
* | |
* <p> | |
* The entire mechanism here feels pretty klunky. Furthermore, there are | |
* several improvements that should be made, including support for | |
* dedicated cryptographic functions that may be present in some browsers; | |
* state files in local storage; cookies containing randomness; etc. So | |
* look for improvements in future versions. | |
* </p> | |
*/ | |
sjcl.random = { | |
/** Generate several random words, and return them in an array | |
* @param {Number} nwords The number of words to generate. | |
*/ | |
randomWords: function (nwords, paranoia) { | |
var out = [], i, readiness = this.isReady(paranoia), g; | |
if (readiness === this._NOT_READY) { | |
throw new sjcl.exception.notready("generator isn't seeded"); | |
} else if (readiness & this._REQUIRES_RESEED) { | |
this._reseedFromPools(!(readiness & this._READY)); | |
} | |
for (i=0; i<nwords; i+= 4) { | |
if ((i+1) % this._MAX_WORDS_PER_BURST === 0) { | |
this._gate(); | |
} | |
g = this._gen4words(); | |
out.push(g[0],g[1],g[2],g[3]); | |
} | |
this._gate(); | |
return out.slice(0,nwords); | |
}, | |
setDefaultParanoia: function (paranoia) { | |
this._defaultParanoia = paranoia; | |
}, | |
/** | |
* Add entropy to the pools. | |
* @param data The entropic value. Should be a 32-bit integer, array of 32-bit integers, or string | |
* @param {Number} estimatedEntropy The estimated entropy of data, in bits | |
* @param {String} source The source of the entropy, eg "mouse" | |
*/ | |
addEntropy: function (data, estimatedEntropy, source) { | |
source = source || "user"; | |
var id, | |
i, ty = 0, tmp, | |
t = (new Date()).valueOf(), | |
robin = this._robins[source], | |
oldReady = this.isReady(); | |
id = this._collectorIds[source]; | |
if (id === undefined) { id = this._collectorIds[source] = this._collectorIdNext ++; } | |
if (robin === undefined) { robin = this._robins[source] = 0; } | |
this._robins[source] = ( this._robins[source] + 1 ) % this._pools.length; | |
switch(typeof(data)) { | |
case "number": | |
data=[data]; | |
ty=1; | |
break; | |
case "object": | |
if (estimatedEntropy === undefined) { | |
/* horrible entropy estimator */ | |
estimatedEntropy = 0; | |
for (i=0; i<data.length; i++) { | |
tmp= data[i]; | |
while (tmp>0) { | |
estimatedEntropy++; | |
tmp = tmp >>> 1; | |
} | |
} | |
} | |
this._pools[robin].update([id,this._eventId++,ty||2,estimatedEntropy,t,data.length].concat(data)); | |
break; | |
case "string": | |
if (estimatedEntropy === undefined) { | |
/* English text has just over 1 bit per character of entropy. | |
* But this might be HTML or something, and have far less | |
* entropy than English... Oh well, let's just say one bit. | |
*/ | |
estimatedEntropy = data.length; | |
} | |
this._pools[robin].update([id,this._eventId++,3,estimatedEntropy,t,data.length]); | |
this._pools[robin].update(data); | |
break; | |
default: | |
throw new sjcl.exception.bug("random: addEntropy only supports number, array or string"); | |
} | |
/* record the new strength */ | |
this._poolEntropy[robin] += estimatedEntropy; | |
this._poolStrength += estimatedEntropy; | |
/* fire off events */ | |
if (oldReady === this._NOT_READY) { | |
if (this.isReady() !== this._NOT_READY) { | |
this._fireEvent("seeded", Math.max(this._strength, this._poolStrength)); | |
} | |
this._fireEvent("progress", this.getProgress()); | |
} | |
}, | |
/** Is the generator ready? */ | |
isReady: function (paranoia) { | |
var entropyRequired = this._PARANOIA_LEVELS[ (paranoia !== undefined) ? paranoia : this._defaultParanoia ]; | |
if (this._strength && this._strength >= entropyRequired) { | |
return (this._poolEntropy[0] > this._BITS_PER_RESEED && (new Date()).valueOf() > this._nextReseed) ? | |
this._REQUIRES_RESEED | this._READY : | |
this._READY; | |
} else { | |
return (this._poolStrength >= entropyRequired) ? | |
this._REQUIRES_RESEED | this._NOT_READY : | |
this._NOT_READY; | |
} | |
}, | |
/** Get the generator's progress toward readiness, as a fraction */ | |
getProgress: function (paranoia) { | |
var entropyRequired = this._PARANOIA_LEVELS[ paranoia ? paranoia : this._defaultParanoia ]; | |
if (this._strength >= entropyRequired) { | |
return 1.0; | |
} else { | |
return (this._poolStrength > entropyRequired) ? | |
1.0 : | |
this._poolStrength / entropyRequired; | |
} | |
}, | |
/** start the built-in entropy collectors */ | |
startCollectors: function () { | |
if (this._collectorsStarted) { return; } | |
if (window.addEventListener) { | |
window.addEventListener("load", this._loadTimeCollector, false); | |
window.addEventListener("mousemove", this._mouseCollector, false); | |
} else if (document.attachEvent) { | |
document.attachEvent("onload", this._loadTimeCollector); | |
document.attachEvent("onmousemove", this._mouseCollector); | |
} | |
else { | |
throw new sjcl.exception.bug("can't attach event"); | |
} | |
this._collectorsStarted = true; | |
}, | |
/** stop the built-in entropy collectors */ | |
stopCollectors: function () { | |
if (!this._collectorsStarted) { return; } | |
if (window.removeEventListener) { | |
window.removeEventListener("load", this._loadTimeCollector); | |
window.removeEventListener("mousemove", this._mouseCollector); | |
} else if (window.detachEvent) { | |
window.detachEvent("onload", this._loadTimeCollector); | |
window.detachEvent("onmousemove", this._mouseCollector); | |
} | |
this._collectorsStarted = false; | |
}, | |
/* use a cookie to store entropy. | |
useCookie: function (all_cookies) { | |
throw new sjcl.exception.bug("random: useCookie is unimplemented"); | |
},*/ | |
/** add an event listener for progress or seeded-ness. */ | |
addEventListener: function (name, callback) { | |
this._callbacks[name][this._callbackI++] = callback; | |
}, | |
/** remove an event listener for progress or seeded-ness */ | |
removeEventListener: function (name, cb) { | |
var i, j, cbs=this._callbacks[name], jsTemp=[]; | |
/* I'm not sure if this is necessary; in C++, iterating over a | |
* collection and modifying it at the same time is a no-no. | |
*/ | |
for (j in cbs) { | |
if (cbs.hasOwnProperty[j] && cbs[j] === cb) { | |
jsTemp.push(j); | |
} | |
} | |
for (i=0; i<jsTemp.length; i++) { | |
j = jsTemp[i]; | |
delete cbs[j]; | |
} | |
}, | |
/* private */ | |
_pools : [new sjcl.hash.sha256()], | |
_poolEntropy : [0], | |
_reseedCount : 0, | |
_robins : {}, | |
_eventId : 0, | |
_collectorIds : {}, | |
_collectorIdNext : 0, | |
_strength : 0, | |
_poolStrength : 0, | |
_nextReseed : 0, | |
_key : [0,0,0,0,0,0,0,0], | |
_counter : [0,0,0,0], | |
_cipher : undefined, | |
_defaultParanoia : 6, | |
/* event listener stuff */ | |
_collectorsStarted : false, | |
_callbacks : {progress: {}, seeded: {}}, | |
_callbackI : 0, | |
/* constants */ | |
_NOT_READY : 0, | |
_READY : 1, | |
_REQUIRES_RESEED : 2, | |
_MAX_WORDS_PER_BURST : 65536, | |
_PARANOIA_LEVELS : [0,48,64,96,128,192,256,384,512,768,1024], | |
_MILLISECONDS_PER_RESEED : 30000, | |
_BITS_PER_RESEED : 80, | |
/** Generate 4 random words, no reseed, no gate. | |
* @private | |
*/ | |
_gen4words: function () { | |
for (var i=0; i<4; i++) { | |
this._counter[i] = this._counter[i]+1 | 0; | |
if (this._counter[i]) { break; } | |
} | |
return this._cipher.encrypt(this._counter); | |
}, | |
/* Rekey the AES instance with itself after a request, or every _MAX_WORDS_PER_BURST words. | |
* @private | |
*/ | |
_gate: function () { | |
this._key = this._gen4words().concat(this._gen4words()); | |
this._cipher = new sjcl.cipher.aes(this._key); | |
}, | |
/** Reseed the generator with the given words | |
* @private | |
*/ | |
_reseed: function (seedWords) { | |
this._key = sjcl.hash.sha256.hash(this._key.concat(seedWords)); | |
this._cipher = new sjcl.cipher.aes(this._key); | |
for (var i=0; i<4; i++) { | |
this._counter[i] = this._counter[i]+1 | 0; | |
if (this._counter[i]) { break; } | |
} | |
}, | |
/** reseed the data from the entropy pools | |
* @param full If set, use all the entropy pools in the reseed. | |
*/ | |
_reseedFromPools: function (full) { | |
var reseedData = [], strength = 0, i; | |
this._nextReseed = reseedData[0] = | |
(new Date()).valueOf() + this._MILLISECONDS_PER_RESEED; | |
for (i=0; i<16; i++) { | |
/* On some browsers, this is cryptographically random. So we might | |
* as well toss it in the pot and stir... | |
*/ | |
reseedData.push(Math.random()*0x100000000|0); | |
} | |
for (i=0; i<this._pools.length; i++) { | |
reseedData = reseedData.concat(this._pools[i].finalize()); | |
strength += this._poolEntropy[i]; | |
this._poolEntropy[i] = 0; | |
if (!full && (this._reseedCount & (1<<i))) { break; } | |
} | |
/* if we used the last pool, push a new one onto the stack */ | |
if (this._reseedCount >= 1 << this._pools.length) { | |
this._pools.push(new sjcl.hash.sha256()); | |
this._poolEntropy.push(0); | |
} | |
/* how strong was this reseed? */ | |
this._poolStrength -= strength; | |
if (strength > this._strength) { | |
this._strength = strength; | |
} | |
this._reseedCount ++; | |
this._reseed(reseedData); | |
}, | |
_mouseCollector: function (ev) { | |
var x = ev.x || ev.clientX || ev.offsetX, y = ev.y || ev.clientY || ev.offsetY; | |
sjcl.random.addEntropy([x,y], 2, "mouse"); | |
}, | |
_loadTimeCollector: function (ev) { | |
var d = new Date(); | |
sjcl.random.addEntropy(d, 2, "loadtime"); | |
}, | |
_fireEvent: function (name, arg) { | |
var j, cbs=sjcl.random._callbacks[name], cbsTemp=[]; | |
/* TODO: there is a race condition between removing collectors and firing them */ | |
/* I'm not sure if this is necessary; in C++, iterating over a | |
* collection and modifying it at the same time is a no-no. | |
*/ | |
for (j in cbs) { | |
if (cbs.hasOwnProperty(j)) { | |
cbsTemp.push(cbs[j]); | |
} | |
} | |
for (j=0; j<cbsTemp.length; j++) { | |
cbsTemp[j](arg); | |
} | |
} | |
}; | |
// This is the initialization | |
CryptoInit(); | |
SetupUsernames(); | |
LoadKeys(); | |
AddElements(); | |
setTimeout(UpdateKeysTable,500); | |
RegisterChangeEvents(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment