Login   Register  
Icontem

File: libs/js/jquery.pidCrypt.js

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Jason Gerfen  >  jQuery.pidCrypt  >  libs/js/jquery.pidCrypt.js  >  Download  
File: libs/js/jquery.pidCrypt.js
Role: Class source
Content type: text/plain
Description: Application script
Class: jQuery.pidCrypt
Encrypt form values using RSA and AES
Author: By
Last change: Wed Jun 13 07:20:09 MDT 2012 - Added new X-Token header value if existing authentication cookie exists
Date: 2012-06-14 04:22
Size: 21,214 bytes
 

Contents

Class file image Download
/**
 *
 * jQuery plugin to impliment RSA public key encryption for
 * form submissions.
 *
 * Utilizes the pidCrypt libraries for client public key
 * encryption while the associated PHP class uses
 * OpenSSL to generate the necessary private/public key pairs used
 * by this plug-in
 *
 * Fork me @ https://www.github.com/jas-/jQuery.pidCrypt
 *
 * Author: Jason Gerfen <jason.gerfen@gmail.com>
 * License: GPL (see LICENSE)
 *
 */

(function($){

 /**
  * @function jQuery.pidCrypt
  * @abstract Plug-in to implement pidCrypt RSA and AES
  *           encryption for public key encryption of
  *           web form elements with support for
  *           client storage options
  * @param method string Method to employ for form ID DOM object
  *                      default, sign, verify, encrypt_sign,
  *                      decrypt_verify, authenticate
  * @param options object options object for specific operations
  *                       cache, debug, callback
  */
 $.fn.pidCrypt = function(method) {

  /**
   * @object defaults
   * @abstract Default set of options for plug-in
   */
  var defaults = defaults || {
   appID:        '',                   // Configurable CSRF token
   storage:      'session',            // Configurable storage mechanism
   formID:       $(this),              // Global object for bound DOM object
   type:         'json',               // Configurable method of communication
   aes:          '',                   // Global object for AES encryption key
   keys:         {},                   // Global object for client keyring
   use:          '',                   // Global object for client public key
   callback:     function(){},         // Configurable callback method on XMLHttpRequest success
   preCallback:  function(){},         // Configurable callback prior to XMLHttpRequest
   errCallback:  function(){}          // Configurable callback on XMLHttpRequest error
  };

  /**
   * @object methods
   * @abstract Plug-in methods
   */
  var methods = methods || {

   /**
    * @function init
    * @abstract Default plug-in method. Requests public key, optionally
    *           uses client storage for key, gathers non-null form elements,
    *           encrypts and sends to server for private key decryption
    */
   init: function(o){
    var opts = _main.__setup(o, defaults);
    $('body').removeData('use');
    _modal.__setup(opts);
    $('#keyring').change(function(){ $('body').data('use', _modal.__e(opts, $(this).val())); opts = _main.__setup(o, defaults); });
    $('#'+opts.formID.attr('id')).on('submit', function(e){
     e.preventDefault();
     _main.__do(opts, _main.__gF(opts));
    });
    return true;
   }
  };

  /**
   * @object main
   * @abstract Handles primary functions
   */
  var _main = _main || {

   /**
    * @object _do
    * @abstract Performs all AJAX requests. Sets additional CSRF and checksum
    *           header fields which can be verified on server
    */
   __do: function(o, a){
    var _data = (typeof a=='object') ? _strings.__serialize(a) : a;
    $.ajax({
     form: o.formID.attr('id'),
     url: o.formID.attr('action'),
     type: o.formID.attr('method'),
     data: _data,
     dataType: o.type,
     crossDomain: (o.type==='jsonp') ? true : false,
     beforeSend: function(xhr){
      xhr = _main.__sH(o, xhr, _data);
      ((o.preCallback)&&($.isFunction(o.preCallback))) ? o.preCallback(xhr) : false;
     },
     success: function(x, status, xhr){
      (x) ? _keys.__hR(x, o) : false;
      ((o.callback)&&($.isFunction(o.callback))) ? o.callback.call(x) : console.log(x);
     },
     error: function(xhr, status, error){
      ((o.errCallback)&&($.isFunction(o.errCallback))) ? o.errCallback.call(xhr, status, error) : false;
     }
    });
   },

   /**
    * @function __setup
    * @abstract Performs simple global setup functions
    */
   __setup: function(o, d){
    var opts = $.extend({}, d, o);
    opts.aes = _encrypt.__sAES();
    opts.keys = _keys.__existing(opts);
    opts.appID = (_validation.__vStr(opts.appID)) ? opts.appID : _keys.__gUUID(null);
    if (_validation.__szCk(opts.keys)<=0){
     _keys.__hK(opts);
    }
    opts.use = ($('body').data('use')) ? $('body').data('use') : _keys.__sK(opts);
    return opts;
   },

   /**
    * @function __gF
    * @abstract Performs object creation of non-null form elements
    */
   __gF: function(o){
    var obj={};
    if (!_validation.__vStr(o.use)) {
     o = _main.__setup(o, defaults);
    }
    $.each($('#'+o.formID.attr('id')+' :input, input:radio:selected, input:checkbox:checked, textarea'), function(k, v){
     if ((_validation.__vStr(v.value))&&(_validation.__vStr(v.name))){
      obj[v.name] = (parseInt(v.value.length)>80) ? _strings.__sSplt(v.value) : v.value;
     }
    });
    return _encrypt.__eO(o, obj);
   },

   /**
    * @function __sH
    * @abstract Sets application specific header options
    */
   __sH: function(o, xhr, _data){
    xhr.setRequestHeader('X-Alt-Referer', o.appID);
    if (_validation.__vStr(_keys.__gT(document.cookie))){
     xhr.setRequestHeader("X-Token",_keys.__gT(document.cookie));
    }
    if (_validation.__vStr(_data)){
     xhr.setRequestHeader('Content-MD5', pidCryptUtil.encodeBase64(pidCrypt.MD5(_data)));
    } else {
     xhr.setRequestHeader('Content-MD5', pidCryptUtil.encodeBase64(pidCrypt.MD5(o.appID)));
    }
    if (!_data){
     xhr.setRequestHeader('Access-Control-Allow-Origin', _keys.__id());
     xhr.setRequestHeader('Access-Control-Allow-Methods', 'POST');
     xhr.setRequestHeader('Content-Type', 'application/json');
    }
    return xhr;
   }
  }

  /**
   * @object keys
   * @abstract Object providing interface for key retrieval and storage
   */
  var _keys = _keys || {

   /**
    * @function __hK
    * @abstract Handles retrieval, encryption and storage of key
    */
   __hK: function(o){
    var y = function(){
     var z = (this) ? this : false;
     var email = ((z)&&(z.email)) ? z.email : o.appID;
     var key = ((z)&&(z.key)) ? z.key : false;
     if (!key) return false;
     var obj = {}; obj[o.appID] = {};
     obj[o.appID]['email'] = _encrypt.__e(o.aes, email, o.appID);
     obj[o.appID]['key'] = _encrypt.__e(o.aes, key, o.appID);
     obj = $.extend({}, obj, _keys.__existing(o));
     _storage.__sI(o.storage, _keys.__id(), JSON.stringify(obj));
    }
    o.callback = y;
    _main.__do(o, {'key': true});
    return true;
   },

   /**
    * @function __sK
    * @abstract Attempts to find email address for user specific key retrieval
    */
   __sK: function(o){
    var _r = false;
    if (_validation.__szCk(o.keys)>0){
     $.each(o.keys, function(a,b){
      var _x = /[0-9a-z-_.]{2,45}\@[0-9a-z-_.]{2,45}\.[a-z]{2,4}/i;
      var _e = _encrypt.__d(o.aes, b['email'], a);
      if (_x.test(_e)){
       _r = _encrypt.__d(o.aes, b['key'], a);
       return false;
      } else {
       _r = _encrypt.__d(o.aes, b['key'], a);
      }
     });
    }
    return _r;
   },

   /**
    * @function existing
    * @abstract Function used to return configured options
    *           as JSON object
    */
   __existing: function(o){
    return (_storage.__gI(o.storage, _keys.__id())) ? JSON.parse(_storage.__gI(o.storage, _keys.__id())) : false;
   },

   /**
    * @function __id
    * @abstract Need an id to associate the public key and other
    *           configuration options with a hostname or url
    */
   __id: function(){
    return (_validation.__vStr(location.host)) ? location.host : (_validation.__vStr(location.hostname)) ? location.hostname : 'localhost';
   },

   /**
    * @function gUUID
    * @abstract Generate a uuid (RFC-4122) string or optional hex
    *           string of specified length
    */
   __gUUID: function(len){
    var chars = '0123456789abcdef'.split('');
    var uuid = [], rnd = Math.random, r;
    uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
    uuid[14] = '4';
    for (var i = 0; i < 36; i++){
     if (!uuid[i]){
      r = 0 | rnd()*16;
      uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r & 0xf];
     }
    }
    return (len!==null) ? uuid.join('').replace(/-/g, '').split('',len).join('') : uuid.join('');
   },

   /**
    * @function strIV
    * @abstract Generate IV from string
    */
   __strIV: function(s){
    return (s) ? encodeURI(s.replace(/-/gi, '').substring(16,Math.ceil(16*s.length)%s.length)) : false;
   },

   /**
    * @function __hR
    * @abstract Searches response for new keyring data if any
    */
   __hR: function(r, o){
    var x = false;
    if (_validation.__szCk(r)>0){
     $.each(r, function(a, b){
      if ((a==='keyring')&&(_validation.__vStr(b['email']))){
       if(!_keys.__hlpr(o, b['email'])){
        var k = _keys.__gUUID(null); var obj = {}; obj[k] = {};
        obj[k]['email'] = _encrypt.__e(o.aes, b['email'], k);
        obj[k]['key'] = _encrypt.__e(o.aes, b['key'], k);
        obj = $.extend({}, obj, _keys.__existing(o));
        _storage.__sI(o.storage, _keys.__id(), JSON.stringify(obj));
       }
      }
     });
    }
    return x;
   },

   /**
    * @function __hlpr
    * @abstract Performs comparison on existing keyring entries for specified
    *           email address
    */
   __hlpr: function(o, e){
    var _r = false;
    $.each(_keys.__existing(o), function(a, b){
     if (_encrypt.__d(o.aes, b['email'], a)==e){
      _r = true;
     }
    });
    return _r;
   },

   /**
    * @function __gT
    * @abstract Obtains authentication token from cookie if it exists
    */
   __gT: function(c){
    var _r=false; var _c = c.split(';');
    $.each(_c, function(a, b){
     if (b.match('token=')){
      _r=b.substr(b.indexOf('=')+1);
     }
    });
    return _r;
   }
  }

  /**
   * @object encrypt
   * @abstract Handles encryption functionality
   */
  var _encrypt = _encrypt || {

   /**
    * @function eO
    * @abstract Calls certParser() on public key, intializes results with
    *           external pidCrypt.RSA object, performs public key encryption
    *           on object and returns results as object
    */
   __eO: function(o, obj){
    var x = {}; var y = _encrypt.__certParser(o.use);
    _encrypt.__iP(y);
    if (_validation.__szCk(obj)>0){
     $.each(obj, function(a, b){
      if (typeof b==='object'){
       x[a]={};
       $.each(b, function(k, v){
        x[a][k] = pidCrypt.RSA.prototype.encrypt(v);
       });
      } else {
       x[a] = pidCrypt.RSA.prototype.encrypt(b);
      }
     });
    } else {
     x = 0;
    }
    return x;
   },

   /**
    * @function sAES
    * @abstract Returns pidCrypt.AES.CBC object for client AES storage
    */
   __sAES: function(){
    return new pidCrypt.AES.CBC();
   },

   /**
    * @function iP
    * @abstract Returns external pidCrypt.RSA object once certParse()
    *           generates necessary bytes from public key
    */
   __iP: function(pub){
    var rsa = false;
    if (pub.b64){
     var x = pidCryptUtil.decodeBase64(pub.b64);
     rsa = new pidCrypt();
     var asn = pidCrypt.ASN1.decode(pidCryptUtil.toByteArray(x));
     var tree = asn.toHexTree();
     pidCrypt.RSA.prototype.setPublicKeyFromASN(tree);
    }
    return rsa;
   },

   /*
    * parse public/private key function
    * (Copyright https://www.pidder.com/pidcrypt/?page=demo_rsa-encryption)
    */
   __certParser: function(cert){
    if (!cert) return false;
    var lines = cert.split('\n');
    var read = false;
    var b64 = false;
    var end = false;
    var flag = '';
    var retObj = {};
    retObj.info = '';
    retObj.salt = '';
    retObj.iv;
    retObj.b64 = '';
    retObj.aes = false;
    retObj.mode = '';
    retObj.bits = 0;
    for(var i=0; i< lines.length; i++){
     flag = lines[i].substr(0,9);
     if(i==1 && flag != 'Proc-Type' && flag.indexOf('M') == 0)//unencrypted cert?
     b64 = true;
     switch(flag){
      case '-----BEGI':
       read = true;
       break;
      case 'Proc-Type':
       if(read)
        retObj.info = lines[i];
        break;
      case 'DEK-Info:':
       if(read){
        var tmp = lines[i].split(',');
        var dek = tmp[0].split(': ');
        var aes = dek[1].split('-');
        retObj.aes = (aes[0] == 'AES')?true:false;
        retObj.mode = aes[2];
        retObj.bits = parseInt(aes[1]);
        retObj.salt = tmp[1].substr(0,16);
        retObj.iv = tmp[1];
       }
       break;
      case '':
       if(read)
        b64 = true;
        break;
      case '-----END ':
       if(read){
        b64 = false;
        read = false;
       }
       break;
      default:
       if(read && b64)
        retObj.b64 += pidCryptUtil.stripLineFeeds(lines[i]);
     }
    }
    return retObj;
   },

   /**
    * @function __e
    * @abstract Encrypts specified string with specified pass & salt
    */
   __e: function(o, d, p){
    return encodeURI(o.encryptText(d, pidCrypt.SHA512(p), {nBits:256, salt:_keys.__strIV(pidCrypt.SHA512(p))}));
   },

   /**
    * @function __d
    * @abstract Decrypts specified string with specified pass & salt
    */
   __d: function(o, d, p){
    return decodeURI(o.decryptText(d, pidCrypt.SHA512(p), {nBits:256, salt:_keys.__strIV(pidCrypt.SHA512(p))}));
   }
  }

  /**
   * @object strings
   * @abstract Handles string processing
   */
  var _strings = _strings || {

   /**
    * @function _serialize
    * @abstract Create serialized string of object
    */
   __serialize: function(args){
    if (_validation.__szCk(args)>0){
     var x='';
     $.each(args, function(a, b){
      if (typeof b==='object'){
       $.each(b, function(c, d){
        x+=a+'['+c+']'+'='+d+'&';
       });
      } else {
       x+=a+'='+b+'&';
      }
     });
     x = x.substring(0, x.length-1);
    } else {
     return false;
    }
    return x;
   },

   /**
    * @function sSplt
    * @abstract Splits string length helper to overcome limitations with RSA
    *           cipher and key sizes
    */
   __sSplt: function(str){
    var t = str.length/80;
    var y = {}; var x=0; var z=80;
    for (var i=0; i<t; i++) {
     if (i>0) { x=x+80; z=z+80; }
     if (str.slice(x, z).length>0) {
      y[i] = str.slice(x, z);
     }
    }
    return y;
   }
  }

  /**
   * @object storage
   * @abstract Object to provide interface to key storage options
   */
  var _storage = _storage || {

   /**
    * @function sI
    * @abstract Proxy function for setting data with specified client storage
    *           option
    */
   __sI: function(type, k, v){
    var x = false;
    type = (_validation.__vStore(type+'Storage')) ? type : 'cookie';
    switch(type) {
     case 'local':
      x = this.__sL(k, v);
      break;
     case 'session':
      x = this.__sS(k, v);
      break;
     case 'cookie':
      x = this.__sC(k, v);
      break;
     default:
      x = this.__sL(k, v);
      break;
    }
    return x;
   },

   /**
    * @function gI
    * @abstract Proxy function for getting data with specified client storage
    *           option
    */
   __gI: function(type, k){
    var x = false;
    type = (_validation.__vStore(type+'Storage')) ? type : 'cookie';
    switch(type) {
     case 'local':
      x = this.__gL(k);
      break;
     case 'session':
      x = this.__gS(k);
      break;
     case 'cookie':
      x = this.__gC(k);
      break;
     default:
      x = this.__gL(k);
      break;
    }
    return x;
   },

   /**
    * @function dI
    * @abstract Proxy function for deleting data with specified client storage
    *           option
    */
   __dI: function(type, k){
    var x = false;
    type = (_validation.__vStore(type+'Storage')) ? type : 'cookie';
    switch(type) {
     case 'local':
      x = this.__dL(k);
      break;
     case 'session':
      x = this.__dS(k);
      break;
     case 'cookie':
      x = this.__dC(k);
      break;
     default:
      x = this.__dL(k);
      break;
    }
    return x;
   },

   /**
    * @function sL
    * @abstract Function used to set localStorage items
    */
   __sL: function(k, v){
    return (localStorage.setItem(k, v)) ? false : true;
   },

   /**
    * @function sS
    * @abstract Function used to set sessionStorage items
    */
   __sS: function(k, v){
    return (sessionStorage.setItem(k, v)) ? false : true;
   },

   /**
    * @function sC
    * @abstract Function used to set cookie items
    */
   __sC: function(k, v){
    if (typeof $.cookie === 'function') {
     return ($.cookie(k, v, {expires: 7})) ? true : false;
    } else {
     return false;
    }
   },

   /**
    * @function gL
    * @abstract Function used to get localStorage items
    */
   __gL: function(k){
    return (localStorage.getItem(k)) ? localStorage.getItem(k) : false;
   },

   /**
    * @function sS
    * @abstract Function used to get sessionStorage items
    */
   __gS: function(k){
    return (sessionStorage.getItem(k)) ? sessionStorage.getItem(k) : false;
   },

   /**
    * @function sC
    * @abstract Function used to get cookie items
    */
   __gC: function(name){
    if (typeof $.cookie === 'function') {
     return ($.cookie(name)) ? $.cookie(name) : false;
    } else {
     return false;
    }
   },

   /**
    * @function dL
    * @abstract Function used to delete localStorage items
    */
   __dL: function(k){
    return (localStorage.removeItem(k)) ? localStorage.removeItem(k) : false;
   },

   /**
    * @function dS
    * @abstract Function used to delete sessionStorage items
    */
   __dS: function(k){
    return (sessionStorage.removeItem(k)) ? sessionStorage.removeItem(k) : false;
   },

   /**
    * @function dC
    * @abstract Function used to delete cookie items
    */
   __dC: function(name){
    if (typeof $.cookie === 'function') {
     return ($.cookie(name, '', {expires: -7})) ? true : false;
    } else {
     return false;
    }
   }
  }

  /**
   * @method validation
   * @abstract Provides interface to validation functionality
   */
  var _validation = _validation || {

   /**
    * @function vStr
    * @abstract Function used combine string checking functions
    */
   __vStr: function(x){
    if (!x) return false;
    return ((x==false)||(x.length==0)||(!x)||(x==null)||(x=='')||(typeof x=='undefined')) ? false : true;
   },

   /**
    * @function vStore
    * @abstract Function used to validate client storage option
    */
   __vStore: function(type){
    try {
     return ((type in window)&&(window[type])) ? true : false;
    } catch (e) {
     return false;
    }
   },

   /**
    * @function szCk
    * @abstract Performs a check on object sizes
    */
   __szCk: function(obj){
    var n = 0;
    $.each(obj, function(k, v){
     if (obj.hasOwnProperty(k)) n++;
    });
    return n;
   }
  }

  /**
   * @method modal
   * @abstract Modal object
   */
  var _modal = _modal || {

   /**
    * @function __setup
    * @abstract Provides preliminary setup for new modal window
    */
   __setup: function(o){
    if (_validation.__szCk(o.keys)>=3){
     var _win = '<div id="overlay"></div><div id="modal"><div id="content">'+_modal.__aK(o)+'</div></div>';
     $('#'+o.formID.attr('name')).prepend(_win);
     $('body').css({'overflow':'hidden'});
     $('#overlay').css({'position':'fixed','top':0,'left':0,'width':'100%','height':'100%','background':'#000','opacity':0.5,'filter':'alpha(opacity=50)'});
     $('#modal').css({'position':'absolute','background':'rgba(0,0,0,0.2)','border-radius':'14px','padding':'8px'});
     $('#content').css({'border-radius':'8px','background':'#fff','padding':'20px'});
    }
   },

   /**
    * @function __e
    * @abstract Executes the decryption and assignment within the global scope
    *           of the user selected public key while closing the modal window
    */
   __e: function(o, e){
    $.each(o.keys, function(a, b){
     if (_encrypt.__d(o.aes, b['email'], a)==e){
      o.use = _encrypt.__d(o.aes, b['key'], a);
      $('#keyring, #content, #modal, #overlay').hide();
      $('body').css({'overflow':'auto'});
     }
    });
    return o.use;
   },

   /**
    * @function __aK
    * @abstract Creates selectable list of current keys
    */
   __aK: function(o){
    var _s = _validation.__szCk(o.keys);
    var _d = (_s>=5) ? 5 : _s;
    var _x, _a = '';
    if (_s>=3){
     var _k = '';
     _x = '<label for="keyring">Select your email:</label><select name="keyring" id="keyring" size="'+_d+'" multiple>';
     $.each(o.keys, function(k, v){
      _k = _encrypt.__d(o.aes, v['email'], k);
      _a = /[0-9a-z-_.]{2,45}\@[0-9a-z-_.]{2,45}\.[a-z]{2,4}/i;
      if (_a.test(_k)){
       _x = _x + '<option value="'+_k+'">'+_k+'</option>';
      }
     });
     _x = _x + '</select>';
    }
    return _x;
   }
  }

  /**
   * @function __r
   * @abstract Function used help debug objects recursively
   */
  var __r = function(obj){
   $.each(obj, function(x,y){
    if (typeof y==='object'){
     console.log(x);
     __r(y);
    } else {
     console.log(x+' => '+y);
    }
   });
  }

  /* robot, do something */
  if (methods[method]){
   return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  } else if ((typeof method==='object')||(!method)){
   return methods.init.apply(this, arguments);
  } else {
   console.log('Method '+method+' does not exist');
  }
 };
})(jQuery);