/**
 * WMSSL - WhoisMall SSL
 * aSSL 을 이용한 웹통신 암호화 솔루션
 * @version 3.1.1
 * @author songbum@whois.co.kr
 */

var WMSSL = {}
WMSSL.debug = false;
WMSSL.isConnected = false;
WMSSL.connectUrl = null;
WMSSL.sessionNS = '__sys_ssl_data';
WMSSL.encForm = '__sys_form_encrypted_data';
WMSSL.rsaPublicKey = '';
WMSSL.ENC_PREFIX = '__ENC__';
WMSSL.ENC_SEPERATOR = '||@||';
WMSSL.ENC_BUCKET_SEPERATOR = '||^||';
WMSSL.BUCKET_SIZE = 2048;

WMSSL.addDebugMessage = function(msg) {
	if (!WMSSL.debug) return;
	if (typeof jQuery == 'undefined') return;
	jQuery(document.body).append('<div>' + msg + '</div>');
}

WMSSL.connect = function(url) {
	var res = WMSSL.getRSAPublicKey(url);
	WMSSL.rsaPublicKey = res;
	WMSSL.connectUrl = url;
	if (WMSSL.rsaPublicKey != '') {
		WMSSL.isConnected = true;
	}
}
WMSSL.getRSAPublicKey = function(url, callback, conn) {
	var oAjax = new WMSSL.Ajax(url);
	oAjax.async = false;
	oAjax.setParam('mode', 'get_rsa_key');
	if (conn) oAjax.setParam('conn', conn);
	var result = '';
	oAjax.send(function(xhr, text){
		result = text;
	});
	oAjax = null;
	return result;
}

WMSSL.encElement = new Array();
WMSSL.addElement = function(val) {
	WMSSL.encElement.push(val);
}
WMSSL.getElements = function() {
	return WMSSL.encElement;
}

WMSSL.decrypt = function() {
	if (!WMSSL.isConnected) return;
	if (!WMSSL.rsaPublicKey) return;
	if (!document.forms[WMSSL.encForm]) return;
	var oForm = document.forms[WMSSL.encForm];
	var aes = new WMSSL.AES();
	var AESKeyClient = aes.generateKey();

	WMSSL.addDebugMessage('<b>AESKeyClient</b> : ' + AESKeyClient);
	
	var rsa = new WMSSL.RSA();
	//WMSSL.addDebugMessage('<b>rsaPublicKey</b> : ' +  WMSSL.rsaPublicKey);
	encAesKey = rsa.encrypt(AESKeyClient, WMSSL.rsaPublicKey);
	//WMSSL.addDebugMessage('<b>encAesKey</b> : ' +  encAesKey);
	var oAjax = new WMSSL.Ajax(WMSSL.connectUrl);
	oAjax.setParam('mode', 'get_aes_key');
	oAjax.setParam('pk', encAesKey);
	oAjax.setParam('sessionKey', oForm.elements['sessionKey'].value);
	var password = '';
	oAjax.send(function(xhr, text){
		password = text;
	});
	//WMSSL.addDebugMessage('<b>AESKeyServer(passwd)</b> : ' + password);
	AESKeyServer = aes.decrypt(password, AESKeyClient);
	WMSSL.addDebugMessage('<b>AESKeyServer</b> : ' + AESKeyServer);
	WMSSL.addDebugMessage('<b>oForm.elements.length</b> : ' + oForm.elements.length);
	for(var i=0; i<oForm.elements.length; i++) {		
		if (oForm.elements[i].nodeName.toLowerCase() == 'input' && oForm.elements[i].getAttribute('type') == 'hidden') {
			var nodeType, accessor, name;
			var _nameToken = oForm.elements[i].name.split('.');
			if (_nameToken.length > 1) {
				if (_nameToken[0].toLowerCase() != 'form') continue;
				nodeType = 'form';
				accessor = _nameToken[1];
				name = _nameToken[2];
			} else {
				nodeType = 'text';
				accessor = oForm.elements[i].name;
			}
			if (i == 0) {
				var value = oForm.elements[i].value;
			}
			else {
				var value = aes.decrypt(oForm.elements[i].value, AESKeyServer);
			}
			WMSSL.addDebugMessage('&nbsp;&nbsp;<b>target ' + i + '</b> : '+ oForm.elements[i].name + '(' + nodeType + ')'  + ' = ' + value);

			switch(nodeType) {
				case 'form':
					if (!document.forms[accessor].elements[name]) continue;
					var _tObj = document.forms[accessor].elements[name];
					var _nodeName;
					if (_tObj.nodeName) {
						_nodeName = _tObj.nodeName.toLowerCase();
					} else if (_tObj.length) {
						_nodeName = _tObj[0].nodeName.toLowerCase();
					} else {
						continue;
					}

					switch(_nodeName) {
						case 'input':
							var _type = '';
							if (_tObj.length) {
								_type = _tObj[0].getAttribute('type').toLowerCase();
							} else {
								_type = _tObj.getAttribute('type').toLowerCase();
							}
							switch(_type) {
								case 'text':
								case 'password':
								case 'hidden':
									_tObj.value = value;
									_tObj.setAttribute('encrypt', 'true');
									break;
								case 'checkbox':
									var _valToken = value.split(WMSSL.ENC_SEPERATOR);
									for(var j=0; j<_tObj.length; j++) {
										if (value.search(_tObj[j].value) > -1) {
											_tObj[j].setAttribute('checked', 'true');
											_tObj[j].setAttribute('encrypt', 'true');
										}
									}
									break;
								case 'radio':
									for(var j=0; j<_tObj.length; j++) {
										if (_tObj[j].value == value) { 
											_tObj[j].setAttribute('checked', 'true');
											_tObj[j].setAttribute('encrypt', 'true');
										}
									}
									break;
								default:
									continue;
									break;
							}
							break;
						case 'select':
							_tObj.setAttribute('encrypt', 'true');
							for(var j=0; j<_tObj.options.length; j++) {
								if (_tObj.options[j].value == value) _tObj[j].selected = 'selected';
							}
							break;
						case 'textarea':
							_tObj.innerHTML = value;
							_tObj.setAttribute('encrypt', 'true');
							break;
					}					
					break;
				case 'text':
					var tgt = document.getElementById(accessor);
					if (!tgt) {
						continue;
					}
					if (tgt.getAttribute('type') == 'hidden') {
						tgt.value = value;
					}
					else {
						tgt.innerHTML = value;
					}
					break;
			}
			oForm.elements[i].value = '';
		}
	}
	WMSSL.DOM.removeMe(oForm);
}
WMSSL.DOM = {
	removeMe:function(frm) {
		frm.parentNode.removeChild(frm);
	}
}

WMSSL.submit = function(form, opts) {
	if (!WMSSL.isConnected) return;
	if (!WMSSL.rsaPublicKey) return;
	if (!opts) opts = {}

	var _mode = (opts.mode)? opts.mode : 'plain';
	var _callback = (opts.callback)? opts.callback : null;
	var aes = new WMSSL.AES();
	var AESKeyClient = aes.generateKey();
	var fe = form.elements;
	var rsa = new WMSSL.RSA();
	encAesKey = rsa.encrypt(AESKeyClient, WMSSL.rsaPublicKey);
	var _sendElm = new Array();
	_sendElm.push({'name':'__sys_dat_enc_key', 'value':encAesKey});
	_sendElm.push({'name':'__sys_dat_mode', 'value':_mode});
	var _files = new Array();
	for(var i=0; i<fe.length; i++) {
		var _ev = '';
		var _et = '';
		var _mustEncrypt = false;
		var _ce = fe[i];
		if (_ce.getAttribute('disabled')) continue;
		var _en = _ce.name;
		if (!_en) continue;
		if (WMSSL.Helper.inArray(WMSSL.getElements(), _en) ) {
			_mustEncrypt = true;

			switch(_ce.nodeName.toLowerCase()) {
				case 'input':
					_et = _ce.getAttribute('type').toLowerCase();
					switch(_et) {
						case 'radio':
						case 'checkbox':
							if (_ce.checked) _ev = _ce.value;
							break;
						case 'button':
						case 'submit':
						case 'cancel':
							continue;
							break;
						case 'text':
						case 'password':
						case 'hidden':
						default:
							_ev = _ce.value;
							break;
					}
					break;

				case 'select':
					if (_ce.options && _ce.options.length > 0) _ev = _ce.options[_ce.selectedIndex].value;
					break;

				case 'textarea':
					_ev = _ce.value;
					break;
			}
		}
		
		var _value = '';
		if (_mustEncrypt) {	
			var _len = _ev.length;
			var _result = _ev;
			if (_len > WMSSL.BUCKET_SIZE) {
				var _cnt = Math.ceil(_len / WMSSL.BUCKET_SIZE);
				var _buff = new Array();
				for(var j=0; j < _cnt; j++) {
					var _start = j * WMSSL.BUCKET_SIZE;
					var _tmp = aes.encrypt(_ev.substr(_start, WMSSL.BUCKET_SIZE), AESKeyClient);
					_buff.push(_tmp);
				}

				_result = _buff.join(WMSSL.ENC_BUCKET_SEPERATOR);
			} else {
				_result = aes.encrypt(_ev, AESKeyClient);
			}
			_value = _result;
			_value = encodeURIComponent(_value);
			if (_ev) {
				var dup = false;
				for(var ii=0; ii<_sendElm.length; ii++) {
					if ( _sendElm[ii].name == WMSSL.ENC_PREFIX+_en ) {
						dup = true;
						_sendElm[ii].value = _sendElm[ii].value+WMSSL.ENC_SEPERATOR+_value;
						break;
					}
				}
				if (dup == false) {
					_sendElm.push({'name':WMSSL.ENC_PREFIX+_en, 'value':_value});
				}
			}
		}
		else {
			_value = _ev;
		}
	}
	switch(_mode) {
		case 'ajax':			
			var oAjax = new WMSSL.Ajax(form.action);
			for(var ii=0; ii<_sendElm.length; ii++) {
				oAjax.setParam(_sendElm[ii].name, _sendElm[ii].value);
			}
			for(var i=0; i<fe.length; i++) {
				_ce = fe[i];
				_en = _ce.name;
				_ev = _ce.value;
				if (!WMSSL.Helper.inArray(WMSSL.getElements(), _en) ) {				
					oAjax.setParam(_en, encodeURIComponent(_ev));
				}
			}
			oAjax.send(_callback);
			break;
		case 'plain':
		default:
			form.onsubmit = function() { return false; }
			for(var i=0; i<fe.length; i++) {
				_ce = fe[i];
				if (WMSSL.Helper.inArray(WMSSL.getElements(), _ce.name) ) {				
					_ce.disabled = true;
				}
			}
			WMSSL.Helper.createSendInput(form, _sendElm);
			form.submit();
			break;
	}
}

WMSSL.Ajax = function(turl) {
	var _params = new Array();

	this.url = turl;
	this.method = 'POST';
	this.contentType = 'application/x-www-form-urlencoded; charset=UTF-8';
	this.async = false;
	this.returnType = 'text';

	this.send = function(callback) {
		var _return;
		var xhr = getHTTPObject();
		if (xhr) {
			xhr.onreadystatechange = function() {
				if (xhr.readyState == 4) {
					if (xhr.status == 200 || xhr.status == 304) {
						var result = null;
						switch(this.returnType) {
							case 'xml':
								result = xhr.resposeXML;
								break;
							case 'json':
								result = eval('(' + xhr.responseText + ')')
								break;
							case 'text':
							default:
								result = xhr.responseText;
								break;
						}
						if (callback) callback(xhr, result);
					}
				}
			}
			xhr.open(this.method.toUpperCase(), this.url, this.async);
			xhr.setRequestHeader('Content-Type', this.contentType);
			xhr.send(createQuery());
			_return = xhr;
		} else {
			_return = false;
		}
		return _return;
	}

	this.setParam = function(key, val) {
		_params.push(key + '=' + val);
	}

	var createQuery = function() {
		var _len = _params.length;
		if (_len < 1) return '';

    	return _params.join('&');
	}

	var getHTTPObject = function() {
		if (window.XMLHttpRequest) return new XMLHttpRequest();
		if (window.ActiveXObject) {
			try {
				return new ActiveXObject("Msxm12.XMLHTTP");
			} catch(e) {
				try	{
					return new ActiveXObject("Microsoft.XMLHTTP");
				} catch(e) {
					return false;
				}
			}
		}
	}
}

WMSSL.Helper = {
	inArray : function(array, keyword) {
		if (typeof array != 'object') return;
		if (array.length <=0 ) return;

		var _l = array.length;
		var _return = false;
		for(var i=0; i<_l; i++) {
			if (array[i] == keyword) {
				_return = true;
				break;
			}
		}
		return _return;
	},

	createSendInput : function(frm, _sendElm) {
		var oForm = frm;
		for(var i=0;i<_sendElm.length; i++) {			
			var inputs = oForm.getElementsByTagName('input');
			var exists = false;
			var _name = _sendElm[i].name;
			var _value = _sendElm[i].value;
			for(var j=0; j<inputs.length; j++) {
				if (inputs[j].getAttribute('name') == _name) {
					exists = true;
					break;
				}
			}
			if (exists) {
				var _vv = oForm.elements[_name].value + WMSSL.ENC_SEPERATOR + _value;
				oForm.elements[_name].value = _vv;
			} else {
				var oInput = document.createElement('input');
				oInput.setAttribute('type', 'hidden');
				oInput.setAttribute('name', _name);
				oInput.value = _value;
				oForm.appendChild(oInput);
			}
		}
	}
}

WMSSL.AES = function() {
	this.bits = 256;
	this.keySize = 16;

	this.generateKey = function(nn) {
		var v, n = !nn || isNaN(nn) ? this.keySize : nn, ret = [];
		for (var x=0; x<n; x++) {
			v = Math.floor(Math.random() * 257);
			if (v == 0 || v == 257) { x--; continue; }
			ret[ret.length] = v;
		}
		var s = '';
  		for (var i=0; i<ret.length; i++) s += ret[i].toString(16);
		return s;
	}

	this.encrypt = function(plaintext, key) {
		return Aes.Ctr.encrypt(plaintext, key, this.bits);
	}

	this.decrypt = function(ciphertext, key) {
		return Aes.Ctr.decrypt(ciphertext, key, this.bits);
	}
}

WMSSL.RSA = function() {
	this.bits = 512;
	this.radix = '10001';

	var _rsakey = new JSBN.RSA.RSAKey();
	var _keys = new Array();
	var _dbit = 256;

	this.encrypt = function(plaintext, pk) {
		var res = pk.split('|');
		_rsakey.setPublic(res[0], res[1]);
		var enc = _rsakey.encrypt(plaintext);
		return JSBN.RSA.linebrk(enc, 256); 
	}

	this.generate = function() {
		_rsakey.generate(this.bits, this.radix);
		_keys['n'] = JSBN.RSA.linebrk(_rsakey.n.toString(16),_dbit);
		_keys['d'] = JSBN.RSA.linebrk(_rsakey.d.toString(16),_dbit);
		_keys['p'] = JSBN.RSA.linebrk(_rsakey.p.toString(16),_dbit);
		_keys['q'] = JSBN.RSA.linebrk(_rsakey.q.toString(16),_dbit);
		_keys['dmp1'] = JSBN.RSA.linebrk(_rsakey.dmp1.toString(16),_dbit);
		_keys['dmq1'] = JSBN.RSA.linebrk(_rsakey.dmq1.toString(16),_dbit);
		_keys['coeff'] = JSBN.RSA.linebrk(_rsakey.coeff.toString(16),_dbit);
		_keys['e'] = this.radix;
		return this;
	}

	this.getKeys = function() {
		return _keys;
	}

	this.setKeys = function(keys) {
		if (typeof keys == 'object') _keys = keys;
	}

	this.setPrivateEx = function(n,e,d,p,q,dmp1,dmq1,coeff) {
		_rsakey.setPrivateEx(n, e, d, p, q, dmp1, dmq1, coeff);
	}

	this.setPrivate = function(n,e,d) {
		_rsakey.setPrivate(n, e, d);
	}

	this.decrypt = function(ctext) {
		return _rsakey.decrypt(ctext);
	}

	this.sendPublicKey = function() {
		if (_keys.length < 1) this.generate();
		return _keys['n'] + '|' + _keys['e'];
	}
}
