Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added old user interface prototypes

  • Loading branch information...
commit 12fe794f9c7fb77c7740d84db6f4f7b812f3b644 1 parent cb550bc
Sebastian Schaetz authored
375 prototypes/ui/v1/index.html
View
@@ -0,0 +1,375 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><head>
+<meta http-equiv="content-type" content="text/html; charset=us-ascii" />
+
+
+<title>Test</title>
+<!--
+
+ colors from http://kuler.adobe.com/#create/fromacolor
+
+ bright: #F0F1F2
+ dark: #232625 modified #383D3C modified #656566
+ extrem highlight: #FF5629
+ bright highlight: #D2D9B8
+ dark highlight: #647362
+
+ at some point we should replace the scrollbars with jscrollpane
+ http://jscrollpane.kelvinluck.com/
+
+-->
+<style>
+
+ body, html
+ {
+ font-family: sans;
+ color: #383D3C;
+ }
+
+ *
+ {
+ padding: 0 0 0 0;
+ margin: 0 0 0 0;
+ }
+
+ #header
+ {
+ background-color: white;
+ color: #232625;
+ padding: 4px 4px 4px 4px;
+ height: 20px;
+ padding-left: 5px;
+ border-bottom: 1px solid #FF5629;
+ }
+
+ #contentcontainer
+ {
+ margin-top: 5px;
+ background-color: #FFFFFF;
+ }
+
+ .layoutcolumns
+ {
+ float: left;
+ border-right: 5px solid #FFFFFF;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ }
+
+ #friendlistcontainer
+ {
+ background-color: white;
+ width: 200px;
+ border-left: 5px solid #FFFFFF;
+ }
+
+ #messagelistcontainer
+ {
+ background-color: white;
+ width: 300px;
+ }
+
+ #mainviewportcontainer
+ {
+ background-color: white;
+ width: 500px;
+ }
+
+/* __________________________________________________________________________ */
+
+ #friendlist
+ {
+ }
+
+ #friendlist li
+ {
+ border: 1px solid #656566;
+ color: #656566;
+ margin: 2px 0px 2px 0px;
+ padding: 2px 4px 2px 25px;
+ font-size: 12px;
+ list-style-type: none;
+ font-weight: bold;
+ cursor: pointer;
+ }
+
+ #friendlist li:hover
+ {
+ background-color: #D2D9B8;
+ }
+
+ #friendlist .invite
+ {
+ background: #F0F1F2 url(../icons/user_go.png) 2px 2px no-repeat;
+ }
+
+ #friendlist .normalm
+ {
+ background: #F0F1F2 url(../icons/user_suit.png) 2px 2px no-repeat;
+ }
+
+ #friendlist .normalf
+ {
+ background: #F0F1F2 url(../icons/user_female.png) 2px 2px no-repeat;
+ }
+
+ #friendlist .ignore
+ {
+ background: #F0F1F2 url(../icons/user_delete.png) 2px 2px no-repeat;
+ }
+
+/* __________________________________________________________________________ */
+
+ #messagelist
+ {
+ }
+
+ #messagelist li
+ {
+ background-color: #F0F1F2;
+ border: 1px solid #656566;
+ color: #656566;
+ margin: 2px 0px 2px 0px;
+ padding: 2px 4px 2px 25px;
+ font-size: 12px;
+ list-style-type: none;
+ font-weight: bold;
+ height: 30px;
+ cursor: pointer;
+ }
+
+ #messagelist li:hover
+ {
+ background-color: #D2D9B8;
+ }
+
+ #messagelist .invite
+ {
+ background: #F0F1F2 url(../icons/email_go.png) 2px 2px no-repeat;
+ }
+
+ #messagelist .normal
+ {
+ background: #F0F1F2 url(../icons/email.png) 2px 2px no-repeat;
+ }
+
+ #messagelist .date
+ {
+ float: right;
+ font-weight: normal;
+ }
+
+ #messagelist .normal .subjectnew
+ {
+ font-size: 10px;
+ }
+
+ #messagelist .normal .subject
+ {
+ font-weight: normal;
+ font-size: 10px;
+ }
+
+ #messagelist .invite .subject
+ {
+ font-size: 10px;
+ color: #FF5629;
+ }
+
+/* __________________________________________________________________________ */
+
+ .mainpage
+ {
+ font-size: 12px;
+ }
+
+ .mainpage h3
+ {
+ font-size: 14px;
+ padding-bottom:4px;
+ }
+
+/* __________________________________________________________________________ */
+
+ #frienddetailpage
+ {
+ border: 1px solid #656566;
+ background-color: #F0F1F2;
+ margin: 2px 0px 2px 0px;
+ padding: 2px 4px 2px 4px;
+ }
+
+/* __________________________________________________________________________ */
+
+
+</style>
+
+<!-- styles needed by jScrollPane -->
+<link type="text/css" href="jquery.jscrollpane.css" rel="stylesheet" media="all" />
+
+<script type="text/javascript" src="jquery.js"></script>
+
+<!-- the mousewheel plugin - optional to provide mousewheel support -->
+<script type="text/javascript" src="jquery.mousewheel.js"></script>
+
+<!-- the jScrollPane script -->
+<script type="text/javascript" src="jquery.jscrollpane.min.js"></script>
+
+
+<script type="text/javascript">
+ $(document).ready(function(){
+ var viewportHeight =
+ window.innerHeight ? window.innerHeight : $(window).height();
+ $('#contentcontainer').css({'height':(viewportHeight-35)+'px'});
+ $('.scroll-pane').jScrollPane({showArrows: true});
+ $(window).resize(function(){
+ var viewportHeight =
+ window.innerHeight ? window.innerHeight : $(window).height();
+ $('#contentcontainer').css({'height':(viewportHeight-35)+'px'});
+ $('.scroll-pane').jScrollPane({showArrows: true});
+ });
+ });
+</script>
+
+
+</head><body>
+
+ <div id="header">
+ <h3>starba.se/<span style="color: rgb(255, 86, 41);">username</span></h3>
+ </div>
+
+ <div id="contentcontainer">
+
+ <div id="friendlistcontainer" class="layoutcolumns scroll-pane">
+ <ul id="friendlist">
+ <li class="normalf">Alice</li>
+ <li class="invite">Bob</li>
+ <li class="ignore">Carol</li>
+ <li class="normalm">Dave</li>
+ <li class="normalf">Eve</li>
+ <li class="normalf">Fran</li>
+ <li class="normalm">Gordon</li>
+ <li class="normalm">Isaac</li>
+ <li class="normalm">Ivan</li>
+ <li class="normalm">Julian</li>
+ <li class="normalf">Mallory</li>
+ <li class="normalf">Matilda</li>
+ <li class="normalm">Oscar</li>
+ <li class="normalm">Pat</li>
+ <li class="normalm">Steve</li>
+ <li class="normalm">Trent</li>
+ <li class="normalm">Victor</li>
+ <li class="normalm">Walter</li>
+ <li class="normalf">Zoe</li>
+ </ul>
+ </div>
+
+ <div id="messagelistcontainer" class="layoutcolumns scroll-pane">
+ <ul id="messagelist">
+ <li class="normal">
+ Alice <span class="date">8:34pm 03.05.2010</span><br/>
+ <span class="subjectnew">This is the subject of a new message</span>
+ </li>
+ <li class="normal">
+ Alice <span class="date">5:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="invite">
+ Dave <span class="date">3:35pm 03.05.2010</span><br/>
+ <span class="subject">This is a new invite</span>
+ </li>
+ <li class="normal">
+ Alice <span class="date">2:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Gordon <span class="date">1:54pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Alice <span class="date">8:34pm 03.05.2010</span><br/>
+ <span class="subjectnew">This is the subject of a new message</span>
+ </li>
+ <li class="normal">
+ Pat <span class="date">5:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Gordon <span class="date">2:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Alice <span class="date">1:54pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Alice <span class="date">8:34pm 03.05.2010</span><br/>
+ <span class="subjectnew">This is the subject of a new message</span>
+ </li>
+ <li class="normal">
+ Walter <span class="date">5:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Walter <span class="date">2:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Pat <span class="date">1:54pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Dave <span class="date">8:34pm 03.05.2010</span><br/>
+ <span class="subjectnew">This is the subject of a new message</span>
+ </li>
+ <li class="normal">
+ Dave <span class="date">5:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Isaac <span class="date">2:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Alice <span class="date">1:54pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Alice <span class="date">8:34pm 03.05.2010</span><br/>
+ <span class="subjectnew">This is the subject of a new message</span>
+ </li>
+ <li class="normal">
+ Steve <span class="date">5:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Alice <span class="date">2:23pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ <li class="normal">
+ Walter <span class="date">1:54pm 03.05.2010</span><br/>
+ <span class="subject">This is the subject of an old message</span>
+ </li>
+ </ul>
+ </div>
+
+ <div id="mainviewportcontainer" class="layoutcolumns scroll-pane">
+ <div id="frienddetailpage" class="mainpage">
+ <h3>Details of user Alice</h3>
+ <table>
+ <tr>
+ <td>URL:</td><td>http://localhost/dev/sg/usr/alice/</td>
+ </tr>
+ <tr>
+ <td>Status:</td><td>Friend</td>
+ </tr>
+ </table>
+ <a href="#">[Send Message]</a>
+ <a href="#">[Close]</a>
+ </div>
+ </div>
+
+ </div>
+
+</body></html>
6,240 prototypes/ui/v1/jquery.js
View
6,240 additions, 0 deletions not shown
120 prototypes/ui/v1/jquery.jscrollpane.css
View
@@ -0,0 +1,120 @@
+/*
+ * CSS Styles that are needed by jScrollPane for it to operate correctly.
+ *
+ * Include this stylesheet in your site or copy and paste the styles below into your stylesheet - jScrollPane
+ * may not operate correctly without them.
+ */
+
+.jspContainer
+{
+ overflow: hidden;
+ position: relative;
+}
+
+.jspPane
+{
+ position: absolute;
+}
+
+.jspVerticalBar
+{
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 10px;
+ height: 100%;
+ background: red;
+}
+
+.jspHorizontalBar
+{
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 10px;
+ background: red;
+}
+
+.jspVerticalBar *,
+.jspHorizontalBar *
+{
+ margin: 0;
+ padding: 0;
+}
+
+.jspCap
+{
+ display: none;
+}
+
+.jspHorizontalBar .jspCap
+{
+ float: left;
+}
+
+.jspTrack
+{
+ background: #656566;
+ position: relative;
+}
+
+.jspDrag
+{
+ background: #D2D9B8;
+ position: relative;
+ top: 0;
+ left: 0;
+ cursor: pointer;
+}
+
+.jspHorizontalBar .jspTrack,
+.jspHorizontalBar .jspDrag
+{
+ float: left;
+ height: 100%;
+}
+
+.jspArrow
+{
+ background: #FF5629;
+ text-indent: -20000px;
+ display: block;
+ cursor: pointer;
+}
+
+.jspArrow.jspDisabled
+{
+ cursor: default;
+ background: #FF5629;
+}
+
+.jspVerticalBar .jspArrow
+{
+ height: 10px;
+}
+
+.jspHorizontalBar .jspArrow
+{
+ width: 10px;
+ float: left;
+ height: 100%;
+}
+
+.jspVerticalBar .jspArrow:focus
+{
+ outline: none;
+}
+
+.jspCorner
+{
+ background: #eeeef4;
+ float: left;
+ height: 100%;
+}
+
+/* Yuk! CSS Hack for IE6 3 pixel bug :( */
+* html .jspCorner
+{
+ margin: 0 -3px 0 0;
+}
11 prototypes/ui/v1/jquery.jscrollpane.min.js
View
@@ -0,0 +1,11 @@
+/*
+ * jScrollPane - v2.0.0beta6 - 2010-10-28
+ * http://jscrollpane.kelvinluck.com/
+ *
+ * Copyright (c) 2010 Kelvin Luck
+ * Dual licensed under the MIT and GPL licenses.
+ */
+(function(b,a,c){b.fn.jScrollPane=function(f){function d(C,L){var au,N=this,V,ah,v,aj,Q,W,y,q,av,aB,ap,i,H,h,j,X,R,al,U,t,A,am,ac,ak,F,l,ao,at,x,aq,aE,g,aA,ag=true,M=true,aD=false,k=false,Z=b.fn.mwheelIntent?"mwheelIntent.jsp":"mousewheel.jsp";aE=C.css("paddingTop")+" "+C.css("paddingRight")+" "+C.css("paddingBottom")+" "+C.css("paddingLeft");g=(parseInt(C.css("paddingLeft"))||0)+(parseInt(C.css("paddingRight"))||0);an(L);function an(aH){var aL,aK,aJ,aG,aF,aI;au=aH;if(V==c){C.css({overflow:"hidden",padding:0});ah=C.innerWidth()+g;v=C.innerHeight();C.width(ah);V=b('<div class="jspPane" />').wrap(b('<div class="jspContainer" />').css({width:ah+"px",height:v+"px"}));C.wrapInner(V.parent());aj=C.find(">.jspContainer");V=aj.find(">.jspPane");V.css("padding",aE)}else{C.css("width","");aI=C.outerWidth()+g!=ah||C.outerHeight()!=v;if(aI){ah=C.innerWidth()+g;v=C.innerHeight();aj.css({width:ah+"px",height:v+"px"})}aA=V.innerWidth();if(!aI&&V.outerWidth()==Q&&V.outerHeight()==W){if(aB||av){V.css("width",aA+"px");C.css("width",(aA+g)+"px")}return}V.css("width","");C.css("width",(ah)+"px");aj.find(">.jspVerticalBar,>.jspHorizontalBar").remove().end()}aL=V.clone().css("position","absolute");aK=b('<div style="width:1px; position: relative;" />').append(aL);b("body").append(aK);Q=Math.max(V.outerWidth(),aL.outerWidth());aK.remove();W=V.outerHeight();y=Q/ah;q=W/v;av=q>1;aB=y>1;if(!(aB||av)){C.removeClass("jspScrollable");V.css({top:0,width:aj.width()-g});n();D();O();w();af()}else{C.addClass("jspScrollable");aJ=au.maintainPosition&&(H||X);if(aJ){aG=ay();aF=aw()}aC();z();E();if(aJ){K(aG);J(aF)}I();ad();if(au.enableKeyboardNavigation){P()}if(au.clickOnTrack){p()}B();if(au.hijackInternalLinks){m()}}if(au.autoReinitialise&&!aq){aq=setInterval(function(){an(au)},au.autoReinitialiseDelay)}else{if(!au.autoReinitialise&&aq){clearInterval(aq)}}C.trigger("jsp-initialised",[aB||av])}function aC(){if(av){aj.append(b('<div class="jspVerticalBar" />').append(b('<div class="jspCap jspCapTop" />'),b('<div class="jspTrack" />').append(b('<div class="jspDrag" />').append(b('<div class="jspDragTop" />'),b('<div class="jspDragBottom" />'))),b('<div class="jspCap jspCapBottom" />')));R=aj.find(">.jspVerticalBar");al=R.find(">.jspTrack");ap=al.find(">.jspDrag");if(au.showArrows){am=b('<a class="jspArrow jspArrowUp" />').bind("mousedown.jsp",az(0,-1)).bind("click.jsp",ax);ac=b('<a class="jspArrow jspArrowDown" />').bind("mousedown.jsp",az(0,1)).bind("click.jsp",ax);if(au.arrowScrollOnHover){am.bind("mouseover.jsp",az(0,-1,am));ac.bind("mouseover.jsp",az(0,1,ac))}ai(al,au.verticalArrowPositions,am,ac)}t=v;aj.find(">.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow").each(function(){t-=b(this).outerHeight()});ap.hover(function(){ap.addClass("jspHover")},function(){ap.removeClass("jspHover")}).bind("mousedown.jsp",function(aF){b("html").bind("dragstart.jsp selectstart.jsp",function(){return false});ap.addClass("jspActive");var s=aF.pageY-ap.position().top;b("html").bind("mousemove.jsp",function(aG){S(aG.pageY-s,false)}).bind("mouseup.jsp mouseleave.jsp",ar);return false});o()}}function o(){al.height(t+"px");H=0;U=au.verticalGutter+al.outerWidth();V.width(ah-U-g);if(R.position().left==0){V.css("margin-left",U+"px")}}function z(){if(aB){aj.append(b('<div class="jspHorizontalBar" />').append(b('<div class="jspCap jspCapLeft" />'),b('<div class="jspTrack" />').append(b('<div class="jspDrag" />').append(b('<div class="jspDragLeft" />'),b('<div class="jspDragRight" />'))),b('<div class="jspCap jspCapRight" />')));ak=aj.find(">.jspHorizontalBar");F=ak.find(">.jspTrack");h=F.find(">.jspDrag");if(au.showArrows){at=b('<a class="jspArrow jspArrowLeft" />').bind("mousedown.jsp",az(-1,0)).bind("click.jsp",ax);x=b('<a class="jspArrow jspArrowRight" />').bind("mousedown.jsp",az(1,0)).bind("click.jsp",ax);if(au.arrowScrollOnHover){at.bind("mouseover.jsp",az(-1,0,at));
+x.bind("mouseover.jsp",az(1,0,x))}ai(F,au.horizontalArrowPositions,at,x)}h.hover(function(){h.addClass("jspHover")},function(){h.removeClass("jspHover")}).bind("mousedown.jsp",function(aF){b("html").bind("dragstart.jsp selectstart.jsp",function(){return false});h.addClass("jspActive");var s=aF.pageX-h.position().left;b("html").bind("mousemove.jsp",function(aG){T(aG.pageX-s,false)}).bind("mouseup.jsp mouseleave.jsp",ar);return false});l=aj.innerWidth();ae()}else{}}function ae(){aj.find(">.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow").each(function(){l-=b(this).outerWidth()});F.width(l+"px");X=0}function E(){if(aB&&av){var aF=F.outerHeight(),s=al.outerWidth();t-=aF;b(ak).find(">.jspCap:visible,>.jspArrow").each(function(){l+=b(this).outerWidth()});l-=s;v-=s;ah-=aF;F.parent().append(b('<div class="jspCorner" />').css("width",aF+"px"));o();ae()}if(aB){V.width((aj.outerWidth()-g)+"px")}W=V.outerHeight();q=W/v;if(aB){ao=1/y*l;if(ao>au.horizontalDragMaxWidth){ao=au.horizontalDragMaxWidth}else{if(ao<au.horizontalDragMinWidth){ao=au.horizontalDragMinWidth}}h.width(ao+"px");j=l-ao;ab(X)}if(av){A=1/q*t;if(A>au.verticalDragMaxHeight){A=au.verticalDragMaxHeight}else{if(A<au.verticalDragMinHeight){A=au.verticalDragMinHeight}}ap.height(A+"px");i=t-A;aa(H)}}function ai(aG,aI,aF,s){var aK="before",aH="after",aJ;if(aI=="os"){aI=/Mac/.test(navigator.platform)?"after":"split"}if(aI==aK){aH=aI}else{if(aI==aH){aK=aI;aJ=aF;aF=s;s=aJ}}aG[aK](aF)[aH](s)}function az(aF,s,aG){return function(){G(aF,s,this,aG);this.blur();return false}}function G(aH,aF,aK,aJ){aK=b(aK).addClass("jspActive");var aI,s=function(){if(aH!=0){T(X+aH*au.arrowButtonSpeed,false)}if(aF!=0){S(H+aF*au.arrowButtonSpeed,false)}},aG=setInterval(s,au.arrowRepeatFreq);s();aI=aJ==c?"mouseup.jsp":"mouseout.jsp";aJ=aJ||b("html");aJ.bind(aI,function(){aK.removeClass("jspActive");clearInterval(aG);aJ.unbind(aI)})}function p(){w();if(av){al.bind("mousedown.jsp",function(aH){if(aH.originalTarget==c||aH.originalTarget==aH.currentTarget){var aG=b(this),s=setInterval(function(){var aI=aG.offset(),aJ=aH.pageY-aI.top;if(H+A<aJ){S(H+au.trackClickSpeed)}else{if(aJ<H){S(H-au.trackClickSpeed)}else{aF()}}},au.trackClickRepeatFreq),aF=function(){s&&clearInterval(s);s=null;b(document).unbind("mouseup.jsp",aF)};b(document).bind("mouseup.jsp",aF);return false}})}if(aB){F.bind("mousedown.jsp",function(aH){if(aH.originalTarget==c||aH.originalTarget==aH.currentTarget){var aG=b(this),s=setInterval(function(){var aI=aG.offset(),aJ=aH.pageX-aI.left;if(X+ao<aJ){T(X+au.trackClickSpeed)}else{if(aJ<X){T(X-au.trackClickSpeed)}else{aF()}}},au.trackClickRepeatFreq),aF=function(){s&&clearInterval(s);s=null;b(document).unbind("mouseup.jsp",aF)};b(document).bind("mouseup.jsp",aF);return false}})}}function w(){F&&F.unbind("mousedown.jsp");al&&al.unbind("mousedown.jsp")}function ar(){b("html").unbind("dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp");ap&&ap.removeClass("jspActive");h&&h.removeClass("jspActive")}function S(s,aF){if(!av){return}if(s<0){s=0}else{if(s>i){s=i}}if(aF==c){aF=au.animateScroll}if(aF){N.animate(ap,"top",s,aa)}else{ap.css("top",s);aa(s)}}function aa(aF){if(aF==c){aF=ap.position().top}aj.scrollTop(0);H=aF;var aI=H==0,aG=H==i,aH=aF/i,s=-aH*(W-v);if(ag!=aI||aD!=aG){ag=aI;aD=aG;C.trigger("jsp-arrow-change",[ag,aD,M,k])}u(aI,aG);V.css("top",s);C.trigger("jsp-scroll-y",[-s,aI,aG])}function T(aF,s){if(!aB){return}if(aF<0){aF=0}else{if(aF>j){aF=j}}if(s==c){s=au.animateScroll}if(s){N.animate(h,"left",aF,ab)}else{h.css("left",aF);ab(aF)}}function ab(aF){if(aF==c){aF=h.position().left}aj.scrollTop(0);X=aF;var aI=X==0,aH=X==j,aG=aF/j,s=-aG*(Q-ah);if(M!=aI||k!=aH){M=aI;k=aH;C.trigger("jsp-arrow-change",[ag,aD,M,k])}r(aI,aH);V.css("left",s);C.trigger("jsp-scroll-x",[-s,aI,aH])}function u(aF,s){if(au.showArrows){am[aF?"addClass":"removeClass"]("jspDisabled");ac[s?"addClass":"removeClass"]("jspDisabled")}}function r(aF,s){if(au.showArrows){at[aF?"addClass":"removeClass"]("jspDisabled");
+x[s?"addClass":"removeClass"]("jspDisabled")}}function J(s,aF){var aG=s/(W-v);S(aG*i,aF)}function K(aF,s){var aG=aF/(Q-ah);T(aG*j,s)}function Y(aR,aM,aG){var aK,aH,aI,s=0,aQ=0,aF,aL,aO,aN,aP;try{aK=b(aR)}catch(aJ){return}aH=aK.outerHeight();aI=aK.outerWidth();aj.scrollTop(0);aj.scrollLeft(0);while(!aK.is(".jspPane")){s+=aK.position().top;aQ+=aK.position().left;aK=aK.offsetParent();if(/^body|html$/i.test(aK[0].nodeName)){return}}aF=aw();aL=aF+v;if(s<aF||aM){aN=s-au.verticalGutter}else{if(s+aH>aL){aN=s-v+aH+au.verticalGutter}}if(aN){J(aN,aG)}viewportLeft=ay();aO=viewportLeft+ah;if(aQ<viewportLeft||aM){aP=aQ-au.horizontalGutter}else{if(aQ+aI>aO){aP=aQ-ah+aI+au.horizontalGutter}}if(aP){K(aP,aG)}}function ay(){return -V.position().left}function aw(){return -V.position().top}function ad(){aj.unbind(Z).bind(Z,function(aI,aJ,aH,aF){var aG=X,s=H;T(X+aH*au.mouseWheelSpeed,false);S(H-aF*au.mouseWheelSpeed,false);return aG==X&&s==H})}function n(){aj.unbind(Z)}function ax(){return false}function I(){V.unbind("focusin.jsp").bind("focusin.jsp",function(s){if(s.target===V[0]){return}Y(s.target,false)})}function D(){V.unbind("focusin.jsp")}function P(){var aF,s;C.attr("tabindex",0).unbind("keydown.jsp").bind("keydown.jsp",function(aJ){if(aJ.target!==C[0]){return}var aH=X,aG=H,aI=aF?2:16;switch(aJ.keyCode){case 40:S(H+aI,false);break;case 38:S(H-aI,false);break;case 34:case 32:J(aw()+Math.max(32,v)-16);break;case 33:J(aw()-v+16);break;case 35:J(W-v);break;case 36:J(0);break;case 39:T(X+aI,false);break;case 37:T(X-aI,false);break}if(!(aH==X&&aG==H)){aF=true;clearTimeout(s);s=setTimeout(function(){aF=false},260);return false}});if(au.hideFocus){C.css("outline","none");if("hideFocus" in aj[0]){C.attr("hideFocus",true)}}else{C.css("outline","");if("hideFocus" in aj[0]){C.attr("hideFocus",false)}}}function O(){C.attr("tabindex","-1").removeAttr("tabindex").unbind("keydown.jsp")}function B(){if(location.hash&&location.hash.length>1){var aG,aF;try{aG=b(location.hash)}catch(s){return}if(aG.length&&V.find(aG)){if(aj.scrollTop()==0){aF=setInterval(function(){if(aj.scrollTop()>0){Y(location.hash,true);b(document).scrollTop(aj.position().top);clearInterval(aF)}},50)}else{Y(location.hash,true);b(document).scrollTop(aj.position().top)}}}}function af(){b("a.jspHijack").unbind("click.jsp-hijack").removeClass("jspHijack")}function m(){af();b("a[href^=#]").addClass("jspHijack").bind("click.jsp-hijack",function(){var s=this.href.split("#"),aF;if(s.length>1){aF=s[1];if(aF.length>0&&V.find("#"+aF).length>0){Y("#"+aF,true);return false}}})}b.extend(N,{reinitialise:function(aF){aF=b.extend({},aF,au);an(aF)},scrollToElement:function(aG,aF,s){Y(aG,aF,s)},scrollTo:function(aG,s,aF){K(aG,aF);J(s,aF)},scrollToX:function(aF,s){K(aF,s)},scrollToY:function(s,aF){J(s,aF)},scrollBy:function(aF,s,aG){N.scrollByX(aF,aG);N.scrollByY(s,aG)},scrollByX:function(s,aG){var aF=ay()+s,aH=aF/(Q-ah);T(aH*j,aG)},scrollByY:function(s,aG){var aF=aw()+s,aH=aF/(W-v);S(aH*i,aG)},animate:function(aF,aI,s,aH){var aG={};aG[aI]=s;aF.animate(aG,{duration:au.animateDuration,ease:au.animateEase,queue:false,step:aH})},getContentPositionX:function(){return ay()},getContentPositionY:function(){return aw()},getIsScrollableH:function(){return aB},getIsScrollableV:function(){return av},getContentPane:function(){return V},scrollToBottom:function(s){S(i,s)},hijackInternalLinks:function(){m()}})}f=b.extend({},b.fn.jScrollPane.defaults,f);var e;this.each(function(){var g=b(this),h=g.data("jsp");if(h){h.reinitialise(f)}else{h=new d(g,f);g.data("jsp",h)}e=e?e.add(g):g});return e};b.fn.jScrollPane.defaults={showArrows:false,maintainPosition:true,clickOnTrack:true,autoReinitialise:false,autoReinitialiseDelay:500,verticalDragMinHeight:0,verticalDragMaxHeight:99999,horizontalDragMinWidth:0,horizontalDragMaxWidth:99999,animateScroll:false,animateDuration:300,animateEase:"linear",hijackInternalLinks:false,verticalGutter:4,horizontalGutter:4,mouseWheelSpeed:10,arrowButtonSpeed:10,arrowRepeatFreq:100,arrowScrollOnHover:false,trackClickSpeed:30,trackClickRepeatFreq:100,verticalArrowPositions:"split",horizontalArrowPositions:"split",enableKeyboardNavigation:true,hideFocus:false}
+})(jQuery,this);
78 prototypes/ui/v1/jquery.mousewheel.js
View
@@ -0,0 +1,78 @@
+/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
+ * Licensed under the MIT License (LICENSE.txt).
+ *
+ * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
+ * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
+ * Thanks to: Seamus Leahy for adding deltaX and deltaY
+ *
+ * Version: 3.0.4
+ *
+ * Requires: 1.2.2+
+ */
+
+(function($) {
+
+var types = ['DOMMouseScroll', 'mousewheel'];
+
+$.event.special.mousewheel = {
+ setup: function() {
+ if ( this.addEventListener ) {
+ for ( var i=types.length; i; ) {
+ this.addEventListener( types[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = handler;
+ }
+ },
+
+ teardown: function() {
+ if ( this.removeEventListener ) {
+ for ( var i=types.length; i; ) {
+ this.removeEventListener( types[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = null;
+ }
+ }
+};
+
+$.fn.extend({
+ mousewheel: function(fn) {
+ return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
+ },
+
+ unmousewheel: function(fn) {
+ return this.unbind("mousewheel", fn);
+ }
+});
+
+
+function handler(event) {
+ var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
+ event = $.event.fix(orgEvent);
+ event.type = "mousewheel";
+
+ // Old school scrollwheel delta
+ if ( event.wheelDelta ) { delta = event.wheelDelta/120; }
+ if ( event.detail ) { delta = -event.detail/3; }
+
+ // New school multidimensional scroll (touchpads) deltas
+ deltaY = delta;
+
+ // Gecko
+ if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
+ deltaY = 0;
+ deltaX = -1*delta;
+ }
+
+ // Webkit
+ if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
+ if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
+
+ // Add event and delta to the front of the arguments
+ args.unshift(event, delta, deltaX, deltaY);
+
+ return $.event.handle.apply(this, args);
+}
+
+})(jQuery);
2,959 prototypes/ui/v2/core.js
View
@@ -0,0 +1,2959 @@
+/** @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;
+ },
+
+ /** @class Something isn't ready. */
+ notReady: function(message) {
+ this.toString = function() { return "NOT READY: "+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) {
+ 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);
+ },
+
+ /**
+ * Extract a number packed into a bit array.
+ * @param {bitArray} a The array to slice.
+ * @param {Number} bstart The offset to the start of the slice, in bits.
+ * @param {Number} length The length of the number to extract.
+ * @return {Number} The requested slice.
+ */
+ extract: function(a, bstart, blength) {
+ // FIXME: this Math.floor is not necessary at all, but for some reason
+ // seems to suppress a bug in the Chromium JIT.
+ var x, sh = Math.floor((-bstart-blength) & 31);
+ if ((bstart + blength - 1 ^ bstart) & -32) {
+ // it crosses a boundary
+ x = (a[bstart/32|0] << (32 - sh)) ^ (a[bstart/32+1|0] >>> sh);
+ } else {
+ // within a single word
+ x = a[bstart/32|0] >>> sh;
+ }
+ return x & ((1<<blength) - 1);
+ },
+
+ /**
+ * 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 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;
+ }
+};
+/** @fileOverview Bit array codec implementations.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+/** @namespace Hexadecimal */
+sjcl.codec.hex = {
+ /** Convert from a bitArray to a hex string. */
+ fromBits: function (arr) {
+ var out = "", i, x;
+ for (i=0; i<arr.length; i++) {
+ out += ((arr[i]|0)+0xF00000000000).toString(16).substr(4);
+ }
+ return out.substr(0, sjcl.bitArray.bitLength(arr)/4);//.replace(/(.{8})/g, "$1 ");
+ },
+ /** Convert from a hex string to a bitArray. */
+ toBits: function (str) {
+ var i, out=[], len;
+ str = str.replace(/\s|0x/g, "");
+ len = str.length;
+ str = str + "00000000";
+ for (i=0; i<str.length; i+=8) {
+ out.push(parseInt(str.substr(i,8),16)^0);
+ }
+ return sjcl.bitArray.clamp(out, len*4);
+ }
+};
+
+/** @fileOverview Bit array codec implementations.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+/** @namespace Base64 encoding/decoding */
+sjcl.codec.base64 = {
+ /** The base64 alphabet.
+ * @private
+ */
+ _chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
+
+ /** Convert from a bitArray to a base64 string. */
+ fromBits: function (arr, _noEquals) {
+ var out = "", i, bits=0, c = sjcl.codec.base64._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
+ for (i=0; out.length * 6 < bl; ) {
+ out += c.charAt((ta ^ arr[i]>>>bits) >>> 26);
+ if (bits < 6) {
+ ta = arr[i] << (6-bits);
+ bits += 26;
+ i++;
+ } else {
+ ta <<= 6;
+ bits -= 6;
+ }
+ }
+ while ((out.length & 3) && !_noEquals) { out += "="; }
+ return out;
+ },
+
+ /** Convert from a base64 string to a bitArray */
+ toBits: function(str) {
+ str = str.replace(/\s|=/g,'');
+ var out = [], i, bits=0, c = sjcl.codec.base64._chars, ta=0, x;
+ for (i=0; i<str.length; i++) {
+ x = c.indexOf(str.charAt(i));
+ if (x < 0) {
+ throw new sjcl.exception.invalid("this isn't base64!");
+ }
+ if (bits > 26) {
+ bits -= 26;
+ out.push(ta ^ x>>>bits);
+ ta = x << (32-bits);
+ } else {
+ bits += 6;
+ ta ^= x << (32-bits);
+ }
+ }
+ if (bits&56) {
+ out.push(sjcl.bitArray.partial(bits&56, ta, 1));
+ }
+ return out;
+ }
+};
+/** @fileOverview Bit array codec implementations.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+/** @namespace Arrays of bytes */
+sjcl.codec.bytes = {
+ /** Convert from a bitArray to an array of bytes. */
+ 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.push(tmp >>> 24);
+ tmp <<= 8;
+ }
+ return out;
+ },
+ /** Convert from an array of bytes to a bitArray. */
+ toBits: function (bytes) {
+ var out = [], i, tmp=0;
+ for (i=0; i<bytes.length; i++) {
+ tmp = tmp << 8 | bytes[i];
+ if ((i&3) === 3) {
+ out.push(tmp);
+ tmp = 0;
+ }
+ }
+ if (i&3) {
+ out.push(sjcl.bitArray.partial(8*(i&3), tmp));
+ }
+ return out;
+ }
+};
+/** @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();
+ }
+};
+
+/**
+ * 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;
+ }
+};
+
+
+/** @fileOverview CCM mode implementation.
+ *
+ * Special thanks to Roy Nicholson for pointing out a bug in our
+ * implementation.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+/** @namespace CTR mode with CBC MAC. */
+sjcl.mode.ccm = {
+ /** The name of the mode.
+ * @constant
+ */
+ name: "ccm",
+
+ /** Encrypt in CCM mode.
+ * @static
+ * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes.
+ * @param {bitArray} plaintext The plaintext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [adata=[]] The authenticated data.
+ * @param {Number} [tlen=64] the desired tag length, in bits.
+ * @return {bitArray} The encrypted data, an array of bytes.
+ */
+ encrypt: function(prf, plaintext, iv, adata, tlen) {
+ var L, i, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
+ tlen = tlen || 64;
+ adata = adata || [];
+
+ if (ivl < 7) {
+ throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
+ }
+
+ // compute the length of the length
+ for (L=2; L<4 && ol >>> 8*L; L++) {}
+ if (L < 15 - ivl) { L = 15-ivl; }
+ iv = w.clamp(iv,8*(15-L));
+
+ // compute the tag
+ tag = sjcl.mode.ccm._computeTag(prf, plaintext, iv, adata, tlen, L);
+
+ // encrypt
+ out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L);
+
+ return w.concat(out.data, out.tag);
+ },
+
+ /** Decrypt in CCM mode.
+ * @static
+ * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes.
+ * @param {bitArray} ciphertext The ciphertext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [[]] adata The authenticated data.
+ * @param {Number} [64] tlen the desired tag length, in bits.
+ * @return {bitArray} The decrypted data.
+ */
+ decrypt: function(prf, ciphertext, iv, adata, tlen) {
+ tlen = tlen || 64;
+ adata = adata || [];
+ var L, i,
+ w=sjcl.bitArray,
+ ivl = w.bitLength(iv) / 8,
+ ol = w.bitLength(ciphertext),
+ out = w.clamp(ciphertext, ol - tlen),
+ tag = w.bitSlice(ciphertext, ol - tlen), tag2;
+
+
+ ol = (ol - tlen) / 8;
+
+ if (ivl < 7) {
+ throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
+ }
+
+ // compute the length of the length
+ for (L=2; L<4 && ol >>> 8*L; L++) {}
+ if (L < 15 - ivl) { L = 15-ivl; }
+ iv = w.clamp(iv,8*(15-L));
+
+ // decrypt
+ out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L);
+
+ // check the tag
+ tag2 = sjcl.mode.ccm._computeTag(prf, out.data, iv, adata, tlen, L);
+ if (!w.equal(out.tag, tag2)) {
+ throw new sjcl.exception.corrupt("ccm: tag doesn't match");
+ }
+
+ return out.data;
+ },
+
+ /* Compute the (unencrypted) authentication tag, according to the CCM specification
+ * @param {Object} prf The pseudorandom function.
+ * @param {bitArray} plaintext The plaintext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} adata The authenticated data.
+ * @param {Number} tlen the desired tag length, in bits.
+ * @return {bitArray} The tag, but not yet encrypted.
+ * @private
+ */
+ _computeTag: function(prf, plaintext, iv, adata, tlen, L) {
+ // compute B[0]
+ var q, mac, field = 0, offset = 24, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
+
+ tlen /= 8;
+
+ // check tag length and message length
+ if (tlen % 2 || tlen < 4 || tlen > 16) {
+ throw new sjcl.exception.invalid("ccm: invalid tag length");
+ }
+
+ if (adata.length > 0xFFFFFFFF || plaintext.length > 0xFFFFFFFF) {
+ // I don't want to deal with extracting high words from doubles.
+ throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
+ }
+
+ // mac the flags
+ mac = [w.partial(8, (adata.length ? 1<<6 : 0) | (tlen-2) << 2 | L-1)];
+
+ // mac the iv and length
+ mac = w.concat(mac, iv);
+ mac[3] |= w.bitLength(plaintext)/8;
+ mac = prf.encrypt(mac);
+
+
+ if (adata.length) {
+ // mac the associated data. start with its length...
+ tmp = w.bitLength(adata)/8;
+ if (tmp <= 0xFEFF) {
+ macData = [w.partial(16, tmp)];
+ } else if (tmp <= 0xFFFFFFFF) {
+ macData = w.concat([w.partial(16,0xFFFE)], [tmp]);
+ } // else ...
+
+ // mac the data itself
+ macData = w.concat(macData, adata);
+ for (i=0; i<macData.length; i += 4) {
+ mac = prf.encrypt(xor(mac, macData.slice(i,i+4).concat([0,0,0])));
+ }
+ }
+
+ // mac the plaintext
+ for (i=0; i<plaintext.length; i+=4) {
+ mac = prf.encrypt(xor(mac, plaintext.slice(i,i+4).concat([0,0,0])));
+ }
+
+ return w.clamp(mac, tlen * 8);
+ },
+
+ /** CCM CTR mode.
+ * Encrypt or decrypt data and tag with the prf in CCM-style CTR mode.
+ * May mutate its arguments.
+ * @param {Object} prf The PRF.
+ * @param {bitArray} data The data to be encrypted or decrypted.
+ * @param {bitArray} iv The initialization vector.
+ * @param {bitArray} tag The authentication tag.
+ * @param {Number} tlen The length of th etag, in bits.
+ * @param {Number} L The CCM L value.
+ * @return {Object} An object with data and tag, the en/decryption of data and tag values.
+ * @private
+ */
+ _ctrMode: function(prf, data, iv, tag, tlen, L) {
+ var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, b, l = data.length, bl=w.bitLength(data);
+
+ // start the ctr
+ ctr = w.concat([w.partial(8,L-1)],iv).concat([0,0,0]).slice(0,4);
+
+ // en/decrypt the tag
+ tag = w.bitSlice(xor(tag,prf.encrypt(ctr)), 0, tlen);
+
+ // en/decrypt the data
+ if (!l) { return {tag:tag, data:[]}; }
+
+ for (i=0; i<l; i+=4) {
+ ctr[3]++;
+ enc = prf.encrypt(ctr);
+ data[i] ^= enc[0];
+ data[i+1] ^= enc[1];
+ data[i+2] ^= enc[2];
+ data[i+3] ^= enc[3];
+ }
+ return { tag:tag, data:w.clamp(data,bl) };
+ }
+};
+/** @fileOverview CBC mode implementation
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+/** @namespace
+ * Dangerous: CBC mode with PKCS#5 padding.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+if (sjcl.beware === undefined) {
+ sjcl.beware = {};
+}
+sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."
+] = function() {
+ sjcl.mode.cbc = {
+ /** The name of the mode.
+ * @constant
+ */
+ name: "cbc",
+
+ /** Encrypt in CBC mode with PKCS#5 padding.
+ * @param {Object} prp The block cipher. It must have a block size of 16 bytes.
+ * @param {bitArray} plaintext The plaintext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [adata=[]] The authenticated data. Must be empty.
+ * @return The encrypted data, an array of bytes.
+ * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits, or if any adata is specified.
+ */
+ encrypt: function(prp, plaintext, iv, adata) {
+ if (adata && adata.length) {
+ throw new sjcl.exception.invalid("cbc can't authenticate data");
+ }
+ if (sjcl.bitArray.bitLength(iv) !== 128) {
+ throw new sjcl.exception.invalid("cbc iv must be 128 bits");
+ }
+ var i,
+ w = sjcl.bitArray,
+ xor = w._xor4,
+ bl = w.bitLength(plaintext),
+ bp = 0,
+ output = [];
+
+ if (bl&7) {
+ throw new sjcl.exception.invalid("pkcs#5 padding only works for multiples of a byte");
+ }
+
+ for (i=0; bp+128 <= bl; i+=4, bp+=128) {
+ /* Encrypt a non-final block */
+ iv = prp.encrypt(xor(iv, plaintext.slice(i,i+4)));
+ output.splice(i,0,iv[0],iv[1],iv[2],iv[3]);
+ }
+
+ /* Construct the pad. */
+ bl = (16 - ((bl >> 3) & 15)) * 0x1010101;
+
+ /* Pad and encrypt. */
+ iv = prp.encrypt(xor(iv,w.concat(plaintext,[bl,bl,bl,bl]).slice(i,i+4)));
+ output.splice(i,0,iv[0],iv[1],iv[2],iv[3]);
+ return output;
+ },
+
+ /** Decrypt in CBC mode.
+ * @param {Object} prp The block cipher. It must have a block size of 16 bytes.
+ * @param {bitArray} ciphertext The ciphertext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [adata=[]] The authenticated data. It must be empty.
+ * @return The decrypted data, an array of bytes.
+ * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits, or if any adata is specified.
+ * @throws {sjcl.exception.corrupt} if if the message is corrupt.
+ */
+ decrypt: function(prp, ciphertext, iv, adata) {
+ if (adata && adata.length) {
+ throw new sjcl.exception.invalid("cbc can't authenticate data");
+ }
+ if (sjcl.bitArray.bitLength(iv) !== 128) {
+ throw new sjcl.exception.invalid("cbc iv must be 128 bits");
+ }
+ if ((sjcl.bitArray.bitLength(ciphertext) & 127) || !ciphertext.length) {
+ throw new sjcl.exception.corrupt("cbc ciphertext must be a positive multiple of the block size");
+ }
+ var i,
+ w = sjcl.bitArray,
+ xor = w._xor4,
+ bi, bo,
+ output = [];
+
+ adata = adata || [];
+
+ for (i=0; i<ciphertext.length; i+=4) {
+ bi = ciphertext.slice(i,i+4);
+ bo = xor(iv,prp.decrypt(bi));
+ output.splice(i,0,bo[0],bo[1],bo[2],bo[3]);
+ iv = bi;
+ }
+
+ /* check and remove the pad */
+ bi = output[i-1] & 255;
+ if (bi == 0 || bi > 16) {
+ throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
+ }
+ bo = bi * 0x1010101;
+ if (!w.equal(w.bitSlice([bo,bo,bo,bo], 0, bi*8),
+ w.bitSlice(output, output.length*32 - bi*8, output.length*32))) {
+ throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
+ }
+
+ return w.bitSlice(output, 0, output.length*32 - bi*8);
+ }
+ };
+};
+/** @fileOverview OCB 2.0 implementation
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+/** @namespace
+ * Phil Rogaway's Offset CodeBook mode, version 2.0.
+ * May be covered by US and international patents.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+sjcl.mode.ocb2 = {
+ /** The name of the mode.
+ * @constant
+ */
+ name: "ocb2",
+
+ /** Encrypt in OCB mode, version 2.0.
+ * @param {Object} prp The block cipher. It must have a block size of 16 bytes.
+ * @param {bitArray} plaintext The plaintext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [adata=[]] The authenticated data.
+ * @param {Number} [tlen=64] the desired tag length, in bits.
+ * @param [false] premac 1 if the authentication data is pre-macced with PMAC.
+ * @return The encrypted data, an array of bytes.
+ * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits.
+ */
+ encrypt: function(prp, plaintext, iv, adata, tlen, premac) {
+ if (sjcl.bitArray.bitLength(iv) !== 128) {
+ throw new sjcl.exception.invalid("ocb iv must be 128 bits");
+ }
+ var i,
+ times2 = sjcl.mode.ocb2._times2,
+ w = sjcl.bitArray,
+ xor = w._xor4,
+ checksum = [0,0,0,0],
+ delta = times2(prp.encrypt(iv)),
+ bi, bl,
+ output = [],
+ pad;
+
+ adata = adata || [];
+ tlen = tlen || 64;
+
+ for (i=0; i+4 < plaintext.length; i+=4) {
+ /* Encrypt a non-final block */
+ bi = plaintext.slice(i,i+4);
+ checksum = xor(checksum, bi);
+ bi = xor(delta,prp.encrypt(xor(delta, bi)));
+ output.splice(i,0,bi[0],bi[1],bi[2],bi[3]);
+ delta = times2(delta);
+ }
+
+ /* Chop out the final block */
+ bi = plaintext.slice(i);
+ bl = w.bitLength(bi);
+ pad = prp.encrypt(xor(delta,[0,0,0,bl]));
+ bi = w.clamp(xor(bi.concat([0,0,0]),pad), bl);
+
+ /* Checksum the final block, and finalize the checksum */
+ checksum = xor(checksum,xor(bi.concat([0,0,0]),pad));
+ checksum = prp.encrypt(xor(checksum,xor(delta,times2(delta))));
+
+ /* MAC the header */
+ if (adata.length) {
+ checksum = xor(checksum, premac ? adata : sjcl.mode.ocb2.pmac(prp, adata));
+ }
+
+ return output.concat(w.concat(bi, w.clamp(checksum, tlen)));
+ },
+
+ /** Decrypt in OCB mode.
+ * @param {Object} prp The block cipher. It must have a block size of 16 bytes.
+ * @param {bitArray} ciphertext The ciphertext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [adata=[]] The authenticated data.
+ * @param {Number} [tlen=64] the desired tag length, in bits.
+ * @param {boolean} [premac=false] true if the authentication data is pre-macced with PMAC.
+ * @return The decrypted data, an array of bytes.
+ * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits.
+ * @throws {sjcl.exception.corrupt} if if the message is corrupt.
+ */
+ decrypt: function(prp, ciphertext, iv, adata, tlen, premac) {
+ if (sjcl.bitArray.bitLength(iv) !== 128) {
+ throw new sjcl.exception.invalid("ocb iv must be 128 bits");
+ }
+ tlen = tlen || 64;
+ var i,
+ times2 = sjcl.mode.ocb2._times2,
+ w = sjcl.bitArray,
+ xor = w._xor4,
+ checksum = [0,0,0,0],
+ delta = times2(prp.encrypt(iv)),
+ bi, bl,
+ len = sjcl.bitArray.bitLength(ciphertext) - tlen,
+ output = [],
+ pad;
+
+ adata = adata || [];
+
+ for (i=0; i+4 < len/32; i+=4) {
+ /* Decrypt a non-final block */
+ bi = xor(delta, prp.decrypt(xor(delta, ciphertext.slice(i,i+4))));
+ checksum = xor(checksum, bi);
+ output.splice(i,0,bi[0],bi[1],bi[2],bi[3]);
+ delta = times2(delta);
+ }
+
+ /* Chop out and decrypt the final block */
+ bl = len-i*32;
+ pad = prp.encrypt(xor(delta,[0,0,0,bl]));
+ bi = xor(pad, w.clamp(ciphertext.slice(i),bl).concat([0,0,0]));
+
+ /* Checksum the final block, and finalize the checksum */
+ checksum = xor(checksum, bi);
+ checksum = prp.encrypt(xor(checksum, xor(delta, times2(delta))));
+
+ /* MAC the header */
+ if (adata.length) {
+ checksum = xor(checksum, premac ? adata : sjcl.mode.ocb2.pmac(prp, adata));
+ }
+
+ if (!w.equal(w.clamp(checksum, tlen), w.bitSlice(ciphertext, len))) {
+ throw new sjcl.exception.corrupt("ocb: tag doesn't match");
+ }
+
+ return output.concat(w.clamp(bi,bl));
+ },
+
+ /** PMAC authentication for OCB associated data.
+ * @param {Object} prp The block cipher. It must have a block size of 16 bytes.
+ * @param {bitArray} adata The authenticated data.
+ */
+ pmac: function(prp, adata) {
+ var i,
+ times2 = sjcl.mode.ocb2._times2,
+ w = sjcl.bitArray,
+ xor = w._xor4,
+ checksum = [0,0,0,0],
+ delta = prp.encrypt([0,0,0,0]),
+ bi;
+
+ delta = xor(delta,times2(times2(delta)));
+
+ for (i=0; i+4<adata.length; i+=4) {
+ delta = times2(delta);
+ checksum = xor(checksum, prp.encrypt(xor(delta, adata.slice(i,i+4))));
+ }
+
+ bi = adata.slice(i);
+ if (w.bitLength(bi) < 128) {
+ delta = xor(delta,times2(delta));
+ bi = w.concat(bi,[0x80000000|0,0,0,0]);
+ }
+ checksum = xor(checksum, bi);
+ return prp.encrypt(xor(times2(xor(delta,times2(delta))), checksum));
+ },
+
+ /** Double a block of words, OCB style.
+ * @private
+ */
+ _times2: function(x) {
+ return [x[0]<<1 ^ x[1]>>>31,
+ x[1]<<1 ^ x[2]>>>31,
+ x[2]<<1 ^ x[3]>>>31,
+ x[3]<<1 ^ (x[0]>>>31)*0x87];
+ }
+};
+/** @fileOverview HMAC implementation.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+/** HMAC with the specified hash function.
+ * @constructor
+ * @param {bitArray} key the key for HMAC.
+ * @param {Object} [hash=sjcl.hash.sha256] The hash function to use.
+ */
+sjcl.misc.hmac = function (key, Hash) {
+ this._hash = Hash = Hash || sjcl.hash.sha256;
+ var exKey = [[],[]], i,
+ bs = Hash.prototype.blockSize / 32;
+ this._baseHash = [new Hash(), new Hash()];
+
+ if (key.length > bs) {
+ key = Hash.hash(key);
+ }
+
+ for (i=0; i<bs; i++) {
+ exKey[0][i] = key[i]^0x36363636;
+ exKey[1][i] = key[i]^0x5C5C5C5C;
+ }
+
+ this._baseHash[0].update(exKey[0]);
+ this._baseHash[1].update(exKey[1]);
+};
+
+/** HMAC with the specified hash function. Also called encrypt since it's a prf.
+ * @param {bitArray|String} data The data to mac.
+ * @param {Codec} [encoding] the encoding function to use.
+ */
+sjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (data, encoding) {
+ var w = new (this._hash)(this._baseHash[0]).update(data, encoding).finalize();
+ return new (this._hash)(this._baseHash[1]).update(w).finalize();
+};
+
+/** @fileOverview Password-based key-derivation function, version 2.0.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+/** Password-Based Key-Derivation Function, version 2.0.
+ *
+ * Generate keys from passwords using PBKDF2-HMAC-SHA256.
+ *
+ * This is the method specified by RSA's PKCS #5 standard.
+ *
+ * @param {bitArray|String} password The password.
+ * @param {bitArray} salt The salt. Should have lots of entropy.
+ * @param {Number} [count=1000] The number of iterations. Higher numbers make the function slower but more secure.
+ * @param {Number} [length] The length of the derived key. Defaults to the
+ output size of the hash function.
+ * @param {Object} [Prff=sjcl.misc.hmac] The pseudorandom function family.
+ * @return {bitArray} the derived key.
+ */
+sjcl.misc.pbkdf2 = function (password, salt, count, length, Prff) {
+ count = count || 1000;
+
+ if (length < 0 || count < 0) {
+ throw sjcl.exception.invalid("invalid params to pbkdf2");
+ }
+
+ if (typeof password === "string") {
+ password = sjcl.codec.utf8String.toBits(password);
+ }
+
+ Prff = Prff || sjcl.misc.hmac;
+
+ var prf = new Prff(password),
+ u, ui, i, j, k, out = [], b = sjcl.bitArray;
+
+ for (k = 1; 32 * out.length < (length || 1); k++) {
+ u = ui = prf.encrypt(b.concat(salt,[k]));
+
+ for (i=1; i<count; i++) {
+ ui = prf.encrypt(ui);
+ for (j=0; j<ui.length; j++) {
+ u[j] ^= ui[j];
+ }
+ }
+
+ out = out.concat(u);
+ }
+
+ if (length) { out = b.clamp(out, length); }
+
+ return out;
+};
+/** @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);
+ }
+ }
+};
+
+/** @fileOverview Convenince functions centered around JSON encapsulation.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+ /** @namespace JSON encapsulation */
+ sjcl.json = {
+ /** Default values for encryption */
+ defaults: { v:1, iter:1000, ks:128, ts:64, mode:"ccm", adata:"", cipher:"aes" },
+
+ /** Simple encryption function.
+ * @param {String|bitArray} password The password or key.
+ * @param {String} plaintext The data to encrypt.
+ * @param {Object} [params] The parameters including tag, iv and salt.
+ * @param {Object} [rp] A returned version with filled-in parameters.
+ * @return {String} The ciphertext.
+ * @throws {sjcl.exception.invalid} if a parameter is invalid.
+ */
+ encrypt: function (password, plaintext, params, rp) {
+ params = params || {};
+ rp = rp || {};
+
+ var j = sjcl.json, p = j._add({ iv: sjcl.random.randomWords(4,0) },
+ j.defaults), tmp, prp;
+ j._add(p, params);
+ if (typeof p.salt === "string") {
+ p.salt = sjcl.codec.base64.toBits(p.salt);
+ }
+ if (typeof p.iv === "string") {
+ p.iv = sjcl.codec.base64.toBits(p.iv);
+ }
+
+ if (!sjcl.mode[p.mode] ||
+ !sjcl.cipher[p.cipher] ||
+ (typeof password === "string" && p.iter <= 100) ||
+ (p.ts !== 64 && p.ts !== 96 && p.ts !== 128) ||
+ (p.ks !== 128 && p.ks !== 192 && p.ks !== 256) ||
+ (p.iv.length < 2 || p.iv.length > 4)) {
+ throw new sjcl.exception.invalid("json encrypt: invalid parameters");
+ }
+
+ if (typeof password === "string") {
+ tmp = sjcl.misc.cachedPbkdf2(password, p);
+ password = tmp.key.slice(0,p.ks/32);
+ p.salt = tmp.salt;
+ }
+ if (typeof plaintext === "string") {
+ plaintext = sjcl.codec.utf8String.toBits(plaintext);
+ }
+ prp = new sjcl.cipher[p.cipher](password);
+
+ /* return the json data */
+ j._add(rp, p);
+ rp.key = password;
+
+ /* do the encryption */
+ p.ct = sjcl.mode[p.mode].encrypt(prp, plaintext, p.iv, p.adata, p.tag);
+
+ //return j.encode(j._subtract(p, j.defaults));
+ return j.encode(p);
+ },
+
+ /** Simple decryption function.
+ * @param {String|bitArray} password The password or key.
+ * @param {String} ciphertext The ciphertext to decrypt.
+ * @param {Object} [params] Additional non-default parameters.
+ * @param {Object} [rp] A returned object with filled parameters.
+ * @return {String} The plaintext.
+ * @throws {sjcl.exception.invalid} if a parameter is invalid.
+ * @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
+ */
+ decrypt: function (password, ciphertext, params, rp) {
+ params = params || {};
+ rp = rp || {};
+
+ var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),j.decode(ciphertext)), params, true), ct, tmp, prp;
+ if (typeof p.salt === "string") {
+ p.salt = sjcl.codec.base64.toBits(p.salt);
+ }
+ if (typeof p.iv === "string") {
+ p.iv = sjcl.codec.base64.toBits(p.iv);
+ }
+
+ if (!sjcl.mode[p.mode] ||
+ !sjcl.cipher[p.cipher] ||
+ (typeof password === "string" && p.iter <= 100) ||
+ (p.ts !== 64 && p.ts !== 96 && p.ts !== 128) ||
+ (p.ks !== 128 && p.ks !== 192 && p.ks !== 256) ||
+ (!p.iv) ||
+ (p.iv.length < 2 || p.iv.length > 4)) {
+ throw new sjcl.exception.invalid("json decrypt: invalid parameters");
+ }
+
+ if (typeof password === "string") {
+ tmp = sjcl.misc.cachedPbkdf2(password, p);
+ password = tmp.key.slice(0,p.ks/32);
+ p.salt = tmp.salt;
+ }
+ prp = new sjcl.cipher[p.cipher](password);
+
+ /* do the decryption */
+ ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, p.adata, p.tag);
+
+ /* return the json data */
+ j._add(rp, p);
+ rp.key = password;
+
+ return sjcl.codec.utf8String.fromBits(ct);
+ },
+
+ /** Encode a flat structure into a JSON string.
+ * @param {Object} obj The structure to encode.
+ * @return {String} A JSON string.
+ * @throws {sjcl.exception.invalid} if obj has a non-alphanumeric property.
+ * @throws {sjcl.exception.bug} if a parameter has an unsupported type.
+ */
+ encode: function (obj) {
+ var i, out='{', comma='';
+ for (i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ if (!i.match(/^[a-z0-9]+$/i)) {
+ throw new sjcl.exception.invalid("json encode: invalid property name");
+ }
+ out += comma + '"' + i + '":';
+ comma = ',';
+
+ switch (typeof obj[i]) {
+ case 'number':
+ case 'boolean':
+ out += obj[i];
+ break;
+
+ case 'string':
+ out += '"' + escape(obj[i]) + '"';
+ break;
+
+ case 'object':
+ out += '"' + sjcl.codec.base64.fromBits(obj[i],1) + '"';
+ break;
+
+ default:
+ throw new sjcl.exception.bug("json encode: unsupported type");
+ }
+ }
+ }
+ return out+'}';
+ },
+
+ /** Decode a simple (flat) JSON string into a structure. The ciphertext,
+ * adata, salt and iv will be base64-decoded.
+ * @param {String} str The string.
+ * @return {Object} The decoded structure.
+ * @throws {sjcl.exception.invalid} if str isn't (simple) JSON.
+ */
+ decode: function (str) {
+ str = str.replace(/\s/g,'');
+ if (!str.match(/^\{.*\}$/)) {
+ throw new sjcl.exception.invalid("json decode: this isn't json!");
+ }
+ var a = str.replace(/^\{|\}$/g, '').split(/,/), out={}, i, m;
+ for (i=0; i<a.length; i++) {
+ if (!(m=a[i].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))) {
+ throw new sjcl.exception.invalid("json decode: this isn't json!");
+ }
+ if (m[3]) {
+ out[m[2]] = parseInt(m[3],10);
+ } else {
+ out[m[2]] = m[2].match(/^(ct|salt|iv)$/) ? sjcl.codec.base64.toBits(m[4]) : unescape(m[4]);
+ }
+ }
+ return out;
+ },
+
+ /** Insert all elements of src into target, modifying and returning target.
+ * @param {Object} target The object to be modified.
+ * @param {Object} src The object to pull data from.
+ * @param {boolean} [requireSame=false] If true, throw an exception if any field of target differs from corresponding field of src.
+ * @return {Object} target.