").appendTo(i),n=i.uniqueId().attr("id");return this._addClass(s,"ui-tooltip-content"),this._addClass(i,"ui-tooltip","ui-widget ui-widget-content"),i.appendTo(this._appendTo(e)),this.tooltips[n]={element:e,tooltip:i}},_find:function(t){var e=t.data("ui-tooltip-id");return e?this.tooltips[e]:null},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_appendTo:function(t){var e=t.closest(".ui-front, dialog");return e.length||(e=this.document[0].body),e},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur"),o=s.element;n.target=n.currentTarget=o[0],e.close(n,!0),t("#"+i).remove(),o.data("ui-tooltip-title")&&(o.attr("title")||o.attr("title",o.data("ui-tooltip-title")),o.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}}),t.uiBackCompat!==!1&&t.widget("ui.tooltip",t.ui.tooltip,{options:{tooltipClass:null},_tooltip:function(){var t=this._superApply(arguments);return this.options.tooltipClass&&t.tooltip.addClass(this.options.tooltipClass),t}}),t.ui.tooltip;var f="ui-effects-",g="ui-effects-style",m="ui-effects-animated",_=t;t.effects={effect:{}},function(t,e){function i(t,e,i){var s=u[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:0>t?0:t>s.max?s.max:t)}function s(i){var s=h(),n=s._rgba=[];return i=i.toLowerCase(),f(l,function(t,o){var a,r=o.re.exec(i),l=r&&o.parse(r),h=o.space||"rgba";return l?(a=s[h](l),s[c[h].cache]=a[c[h].cache],n=s._rgba=a._rgba,!1):e}),n.length?("0,0,0,0"===n.join()&&t.extend(n,o.transparent),s):o[i]}function n(t,e,i){return i=(i+1)%1,1>6*i?t+6*(e-t)*i:1>2*i?e:2>3*i?t+6*(e-t)*(2/3-i):t}var o,a="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,l=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[t[1],t[2],t[3],t[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[2.55*t[1],2.55*t[2],2.55*t[3],t[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(t){return[t[1],t[2]/100,t[3]/100,t[4]]}}],h=t.Color=function(e,i,s,n){return new t.Color.fn.parse(e,i,s,n)},c={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},u={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},d=h.support={},p=t("
")[0],f=t.each;p.style.cssText="background-color:rgba(1,1,1,.5)",d.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(c,function(t,e){e.cache="_"+t,e.props.alpha={idx:3,type:"percent",def:1}}),h.fn=t.extend(h.prototype,{parse:function(n,a,r,l){if(n===e)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=t(n).css(a),a=e);var u=this,d=t.type(n),p=this._rgba=[];return a!==e&&(n=[n,a,r,l],d="array"),"string"===d?this.parse(s(n)||o._default):"array"===d?(f(c.rgba.props,function(t,e){p[e.idx]=i(n[e.idx],e)}),this):"object"===d?(n instanceof h?f(c,function(t,e){n[e.cache]&&(u[e.cache]=n[e.cache].slice())}):f(c,function(e,s){var o=s.cache;f(s.props,function(t,e){if(!u[o]&&s.to){if("alpha"===t||null==n[t])return;u[o]=s.to(u._rgba)}u[o][e.idx]=i(n[t],e,!0)}),u[o]&&0>t.inArray(null,u[o].slice(0,3))&&(u[o][3]=1,s.from&&(u._rgba=s.from(u[o])))}),this):e},is:function(t){var i=h(t),s=!0,n=this;return f(c,function(t,o){var a,r=i[o.cache];return r&&(a=n[o.cache]||o.to&&o.to(n._rgba)||[],f(o.props,function(t,i){return null!=r[i.idx]?s=r[i.idx]===a[i.idx]:e})),s}),s},_space:function(){var t=[],e=this;return f(c,function(i,s){e[s.cache]&&t.push(i)}),t.pop()},transition:function(t,e){var s=h(t),n=s._space(),o=c[n],a=0===this.alpha()?h("transparent"):this,r=a[o.cache]||o.to(a._rgba),l=r.slice();return s=s[o.cache],f(o.props,function(t,n){var o=n.idx,a=r[o],h=s[o],c=u[n.type]||{};null!==h&&(null===a?l[o]=h:(c.mod&&(h-a>c.mod/2?a+=c.mod:a-h>c.mod/2&&(a-=c.mod)),l[o]=i((h-a)*e+a,n)))}),this[n](l)},blend:function(e){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=h(e)._rgba;return h(t.map(i,function(t,e){return(1-s)*n[e]+s*t}))},toRgbaString:function(){var e="rgba(",i=t.map(this._rgba,function(t,e){return null==t?e>2?1:0:t});return 1===i[3]&&(i.pop(),e="rgb("),e+i.join()+")"},toHslaString:function(){var e="hsla(",i=t.map(this.hsla(),function(t,e){return null==t&&(t=e>2?1:0),e&&3>e&&(t=Math.round(100*t)+"%"),t});return 1===i[3]&&(i.pop(),e="hsl("),e+i.join()+")"},toHexString:function(e){var i=this._rgba.slice(),s=i.pop();return e&&i.push(~~(255*s)),"#"+t.map(i,function(t){return t=(t||0).toString(16),1===t.length?"0"+t:t}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),h.fn.parse.prototype=h.fn,c.hsla.to=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e,i,s=t[0]/255,n=t[1]/255,o=t[2]/255,a=t[3],r=Math.max(s,n,o),l=Math.min(s,n,o),h=r-l,c=r+l,u=.5*c;return e=l===r?0:s===r?60*(n-o)/h+360:n===r?60*(o-s)/h+120:60*(s-n)/h+240,i=0===h?0:.5>=u?h/c:h/(2-c),[Math.round(e)%360,i,u,null==a?1:a]},c.hsla.from=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/360,i=t[1],s=t[2],o=t[3],a=.5>=s?s*(1+i):s+i-s*i,r=2*s-a;return[Math.round(255*n(r,a,e+1/3)),Math.round(255*n(r,a,e)),Math.round(255*n(r,a,e-1/3)),o]},f(c,function(s,n){var o=n.props,a=n.cache,l=n.to,c=n.from;h.fn[s]=function(s){if(l&&!this[a]&&(this[a]=l(this._rgba)),s===e)return this[a].slice();var n,r=t.type(s),u="array"===r||"object"===r?s:arguments,d=this[a].slice();return f(o,function(t,e){var s=u["object"===r?t:e.idx];null==s&&(s=d[e.idx]),d[e.idx]=i(s,e)}),c?(n=h(c(d)),n[a]=d,n):h(d)},f(o,function(e,i){h.fn[e]||(h.fn[e]=function(n){var o,a=t.type(n),l="alpha"===e?this._hsla?"hsla":"rgba":s,h=this[l](),c=h[i.idx];return"undefined"===a?c:("function"===a&&(n=n.call(this,c),a=t.type(n)),null==n&&i.empty?this:("string"===a&&(o=r.exec(n),o&&(n=c+parseFloat(o[2])*("+"===o[1]?1:-1))),h[i.idx]=n,this[l](h)))})})}),h.hook=function(e){var i=e.split(" ");f(i,function(e,i){t.cssHooks[i]={set:function(e,n){var o,a,r="";if("transparent"!==n&&("string"!==t.type(n)||(o=s(n)))){if(n=h(o||n),!d.rgba&&1!==n._rgba[3]){for(a="backgroundColor"===i?e.parentNode:e;(""===r||"transparent"===r)&&a&&a.style;)try{r=t.css(a,"backgroundColor"),a=a.parentNode}catch(l){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{e.style[i]=n}catch(l){}}},t.fx.step[i]=function(e){e.colorInit||(e.start=h(e.elem,i),e.end=h(e.end),e.colorInit=!0),t.cssHooks[i].set(e.elem,e.start.transition(e.end,e.pos))}})},h.hook(a),t.cssHooks.borderColor={expand:function(t){var e={};return f(["Top","Right","Bottom","Left"],function(i,s){e["border"+s+"Color"]=t}),e}},o=t.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(_),function(){function e(e){var i,s,n=e.ownerDocument.defaultView?e.ownerDocument.defaultView.getComputedStyle(e,null):e.currentStyle,o={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(o[t.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(o[i]=n[i]);return o}function i(e,i){var s,o,a={};for(s in i)o=i[s],e[s]!==o&&(n[s]||(t.fx.step[s]||!isNaN(parseFloat(o)))&&(a[s]=o));return a}var s=["add","remove","toggle"],n={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};t.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(e,i){t.fx.step[i]=function(t){("none"!==t.end&&!t.setAttr||1===t.pos&&!t.setAttr)&&(_.style(t.elem,i,t.end),t.setAttr=!0)}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.effects.animateClass=function(n,o,a,r){var l=t.speed(o,a,r);return this.queue(function(){var o,a=t(this),r=a.attr("class")||"",h=l.children?a.find("*").addBack():a;h=h.map(function(){var i=t(this);return{el:i,start:e(this)}}),o=function(){t.each(s,function(t,e){n[e]&&a[e+"Class"](n[e])})},o(),h=h.map(function(){return this.end=e(this.el[0]),this.diff=i(this.start,this.end),this}),a.attr("class",r),h=h.map(function(){var e=this,i=t.Deferred(),s=t.extend({},l,{queue:!1,complete:function(){i.resolve(e)}});return this.el.animate(this.diff,s),i.promise()}),t.when.apply(t,h.get()).done(function(){o(),t.each(arguments,function(){var e=this.el;t.each(this.diff,function(t){e.css(t,"")})}),l.complete.call(a[0])})})},t.fn.extend({addClass:function(e){return function(i,s,n,o){return s?t.effects.animateClass.call(this,{add:i},s,n,o):e.apply(this,arguments)}}(t.fn.addClass),removeClass:function(e){return function(i,s,n,o){return arguments.length>1?t.effects.animateClass.call(this,{remove:i},s,n,o):e.apply(this,arguments)}}(t.fn.removeClass),toggleClass:function(e){return function(i,s,n,o,a){return"boolean"==typeof s||void 0===s?n?t.effects.animateClass.call(this,s?{add:i}:{remove:i},n,o,a):e.apply(this,arguments):t.effects.animateClass.call(this,{toggle:i},s,n,o)}}(t.fn.toggleClass),switchClass:function(e,i,s,n,o){return t.effects.animateClass.call(this,{add:i,remove:e},s,n,o)}})}(),function(){function e(e,i,s,n){return t.isPlainObject(e)&&(i=e,e=e.effect),e={effect:e},null==i&&(i={}),t.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||t.fx.speeds[i])&&(n=s,s=i,i={}),t.isFunction(s)&&(n=s,s=null),i&&t.extend(e,i),s=s||i.duration,e.duration=t.fx.off?0:"number"==typeof s?s:s in t.fx.speeds?t.fx.speeds[s]:t.fx.speeds._default,e.complete=n||i.complete,e}function i(e){return!e||"number"==typeof e||t.fx.speeds[e]?!0:"string"!=typeof e||t.effects.effect[e]?t.isFunction(e)?!0:"object"!=typeof e||e.effect?!1:!0:!0}function s(t,e){var i=e.outerWidth(),s=e.outerHeight(),n=/^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/,o=n.exec(t)||["",0,i,s,0];return{top:parseFloat(o[1])||0,right:"auto"===o[2]?i:parseFloat(o[2]),bottom:"auto"===o[3]?s:parseFloat(o[3]),left:parseFloat(o[4])||0}}t.expr&&t.expr.filters&&t.expr.filters.animated&&(t.expr.filters.animated=function(e){return function(i){return!!t(i).data(m)||e(i)}}(t.expr.filters.animated)),t.uiBackCompat!==!1&&t.extend(t.effects,{save:function(t,e){for(var i=0,s=e.length;s>i;i++)null!==e[i]&&t.data(f+e[i],t[0].style[e[i]])},restore:function(t,e){for(var i,s=0,n=e.length;n>s;s++)null!==e[s]&&(i=t.data(f+e[s]),t.css(e[s],i))},setMode:function(t,e){return"toggle"===e&&(e=t.is(":hidden")?"show":"hide"),e},createWrapper:function(e){if(e.parent().is(".ui-effects-wrapper"))return e.parent();var i={width:e.outerWidth(!0),height:e.outerHeight(!0),"float":e.css("float")},s=t("
").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:e.width(),height:e.height()},o=document.activeElement;try{o.id}catch(a){o=document.body}return e.wrap(s),(e[0]===o||t.contains(e[0],o))&&t(o).trigger("focus"),s=e.parent(),"static"===e.css("position")?(s.css({position:"relative"}),e.css({position:"relative"})):(t.extend(i,{position:e.css("position"),zIndex:e.css("z-index")}),t.each(["top","left","bottom","right"],function(t,s){i[s]=e.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),e.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),e.css(n),s.css(i).show()},removeWrapper:function(e){var i=document.activeElement;return e.parent().is(".ui-effects-wrapper")&&(e.parent().replaceWith(e),(e[0]===i||t.contains(e[0],i))&&t(i).trigger("focus")),e}}),t.extend(t.effects,{version:"1.12.1",define:function(e,i,s){return s||(s=i,i="effect"),t.effects.effect[e]=s,t.effects.effect[e].mode=i,s},scaledDimensions:function(t,e,i){if(0===e)return{height:0,width:0,outerHeight:0,outerWidth:0};var s="horizontal"!==i?(e||100)/100:1,n="vertical"!==i?(e||100)/100:1;return{height:t.height()*n,width:t.width()*s,outerHeight:t.outerHeight()*n,outerWidth:t.outerWidth()*s}},clipToBox:function(t){return{width:t.clip.right-t.clip.left,height:t.clip.bottom-t.clip.top,left:t.clip.left,top:t.clip.top}},unshift:function(t,e,i){var s=t.queue();e>1&&s.splice.apply(s,[1,0].concat(s.splice(e,i))),t.dequeue()},saveStyle:function(t){t.data(g,t[0].style.cssText)},restoreStyle:function(t){t[0].style.cssText=t.data(g)||"",t.removeData(g)},mode:function(t,e){var i=t.is(":hidden");return"toggle"===e&&(e=i?"show":"hide"),(i?"hide"===e:"show"===e)&&(e="none"),e},getBaseline:function(t,e){var i,s;switch(t[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=t[0]/e.height}switch(t[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=t[1]/e.width}return{x:s,y:i}},createPlaceholder:function(e){var i,s=e.css("position"),n=e.position();return e.css({marginTop:e.css("marginTop"),marginBottom:e.css("marginBottom"),marginLeft:e.css("marginLeft"),marginRight:e.css("marginRight")}).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()),/^(static|relative)/.test(s)&&(s="absolute",i=t("<"+e[0].nodeName+">").insertAfter(e).css({display:/^(inline|ruby)/.test(e.css("display"))?"inline-block":"block",visibility:"hidden",marginTop:e.css("marginTop"),marginBottom:e.css("marginBottom"),marginLeft:e.css("marginLeft"),marginRight:e.css("marginRight"),"float":e.css("float")}).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).addClass("ui-effects-placeholder"),e.data(f+"placeholder",i)),e.css({position:s,left:n.left,top:n.top}),i},removePlaceholder:function(t){var e=f+"placeholder",i=t.data(e);i&&(i.remove(),t.removeData(e))},cleanUp:function(e){t.effects.restoreStyle(e),t.effects.removePlaceholder(e)},setTransition:function(e,i,s,n){return n=n||{},t.each(i,function(t,i){var o=e.cssUnit(i);o[0]>0&&(n[i]=o[0]*s+o[1])}),n}}),t.fn.extend({effect:function(){function i(e){function i(){r.removeData(m),t.effects.cleanUp(r),"hide"===s.mode&&r.hide(),a()}function a(){t.isFunction(l)&&l.call(r[0]),t.isFunction(e)&&e()}var r=t(this);s.mode=c.shift(),t.uiBackCompat===!1||o?"none"===s.mode?(r[h](),a()):n.call(r[0],s,i):(r.is(":hidden")?"hide"===h:"show"===h)?(r[h](),a()):n.call(r[0],s,a)}var s=e.apply(this,arguments),n=t.effects.effect[s.effect],o=n.mode,a=s.queue,r=a||"fx",l=s.complete,h=s.mode,c=[],u=function(e){var i=t(this),s=t.effects.mode(i,h)||o;i.data(m,!0),c.push(s),o&&("show"===s||s===o&&"hide"===s)&&i.show(),o&&"none"===s||t.effects.saveStyle(i),t.isFunction(e)&&e()};return t.fx.off||!n?h?this[h](s.duration,l):this.each(function(){l&&l.call(this)}):a===!1?this.each(u).each(i):this.queue(r,u).queue(r,i)},show:function(t){return function(s){if(i(s))return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="show",this.effect.call(this,n)}}(t.fn.show),hide:function(t){return function(s){if(i(s))return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(t.fn.hide),toggle:function(t){return function(s){if(i(s)||"boolean"==typeof s)return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(t.fn.toggle),cssUnit:function(e){var i=this.css(e),s=[];return t.each(["em","px","%","pt"],function(t,e){i.indexOf(e)>0&&(s=[parseFloat(i),e])}),s},cssClip:function(t){return t?this.css("clip","rect("+t.top+"px "+t.right+"px "+t.bottom+"px "+t.left+"px)"):s(this.css("clip"),this)},transfer:function(e,i){var s=t(this),n=t(e.to),o="fixed"===n.css("position"),a=t("body"),r=o?a.scrollTop():0,l=o?a.scrollLeft():0,h=n.offset(),c={top:h.top-r,left:h.left-l,height:n.innerHeight(),width:n.innerWidth()},u=s.offset(),d=t("
").appendTo("body").addClass(e.className).css({top:u.top-r,left:u.left-l,height:s.innerHeight(),width:s.innerWidth(),position:o?"fixed":"absolute"}).animate(c,e.duration,e.easing,function(){d.remove(),t.isFunction(i)&&i()})}}),t.fx.step.clip=function(e){e.clipInit||(e.start=t(e.elem).cssClip(),"string"==typeof e.end&&(e.end=s(e.end,e.elem)),e.clipInit=!0),t(e.elem).cssClip({top:e.pos*(e.end.top-e.start.top)+e.start.top,right:e.pos*(e.end.right-e.start.right)+e.start.right,bottom:e.pos*(e.end.bottom-e.start.bottom)+e.start.bottom,left:e.pos*(e.end.left-e.start.left)+e.start.left})}}(),function(){var e={};t.each(["Quad","Cubic","Quart","Quint","Expo"],function(t,i){e[i]=function(e){return Math.pow(e,t+2)}}),t.extend(e,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;((e=Math.pow(2,--i))-1)/11>t;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),t.each(e,function(e,i){t.easing["easeIn"+e]=i,t.easing["easeOut"+e]=function(t){return 1-i(1-t)},t.easing["easeInOut"+e]=function(t){return.5>t?i(2*t)/2:1-i(-2*t+2)/2}})}();var v=t.effects;t.effects.define("blind","hide",function(e,i){var s={up:["bottom","top"],vertical:["bottom","top"],down:["top","bottom"],left:["right","left"],horizontal:["right","left"],right:["left","right"]},n=t(this),o=e.direction||"up",a=n.cssClip(),r={clip:t.extend({},a)},l=t.effects.createPlaceholder(n);r.clip[s[o][0]]=r.clip[s[o][1]],"show"===e.mode&&(n.cssClip(r.clip),l&&l.css(t.effects.clipToBox(r)),r.clip=a),l&&l.animate(t.effects.clipToBox(r),e.duration,e.easing),n.animate(r,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("bounce",function(e,i){var s,n,o,a=t(this),r=e.mode,l="hide"===r,h="show"===r,c=e.direction||"up",u=e.distance,d=e.times||5,p=2*d+(h||l?1:0),f=e.duration/p,g=e.easing,m="up"===c||"down"===c?"top":"left",_="up"===c||"left"===c,v=0,b=a.queue().length;for(t.effects.createPlaceholder(a),o=a.css(m),u||(u=a["top"===m?"outerHeight":"outerWidth"]()/3),h&&(n={opacity:1},n[m]=o,a.css("opacity",0).css(m,_?2*-u:2*u).animate(n,f,g)),l&&(u/=Math.pow(2,d-1)),n={},n[m]=o;d>v;v++)s={},s[m]=(_?"-=":"+=")+u,a.animate(s,f,g).animate(n,f,g),u=l?2*u:u/2;l&&(s={opacity:0},s[m]=(_?"-=":"+=")+u,a.animate(s,f,g)),a.queue(i),t.effects.unshift(a,b,p+1)}),t.effects.define("clip","hide",function(e,i){var s,n={},o=t(this),a=e.direction||"vertical",r="both"===a,l=r||"horizontal"===a,h=r||"vertical"===a;s=o.cssClip(),n.clip={top:h?(s.bottom-s.top)/2:s.top,right:l?(s.right-s.left)/2:s.right,bottom:h?(s.bottom-s.top)/2:s.bottom,left:l?(s.right-s.left)/2:s.left},t.effects.createPlaceholder(o),"show"===e.mode&&(o.cssClip(n.clip),n.clip=s),o.animate(n,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("drop","hide",function(e,i){var s,n=t(this),o=e.mode,a="show"===o,r=e.direction||"left",l="up"===r||"down"===r?"top":"left",h="up"===r||"left"===r?"-=":"+=",c="+="===h?"-=":"+=",u={opacity:0};t.effects.createPlaceholder(n),s=e.distance||n["top"===l?"outerHeight":"outerWidth"](!0)/2,u[l]=h+s,a&&(n.css(u),u[l]=c+s,u.opacity=1),n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("explode","hide",function(e,i){function s(){b.push(this),b.length===u*d&&n()}function n(){p.css({visibility:"visible"}),t(b).remove(),i()}var o,a,r,l,h,c,u=e.pieces?Math.round(Math.sqrt(e.pieces)):3,d=u,p=t(this),f=e.mode,g="show"===f,m=p.show().css("visibility","hidden").offset(),_=Math.ceil(p.outerWidth()/d),v=Math.ceil(p.outerHeight()/u),b=[];for(o=0;u>o;o++)for(l=m.top+o*v,c=o-(u-1)/2,a=0;d>a;a++)r=m.left+a*_,h=a-(d-1)/2,p.clone().appendTo("body").wrap("
").css({position:"absolute",visibility:"visible",left:-a*_,top:-o*v}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:_,height:v,left:r+(g?h*_:0),top:l+(g?c*v:0),opacity:g?0:1}).animate({left:r+(g?0:h*_),top:l+(g?0:c*v),opacity:g?1:0},e.duration||500,e.easing,s)}),t.effects.define("fade","toggle",function(e,i){var s="show"===e.mode;t(this).css("opacity",s?0:1).animate({opacity:s?1:0},{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("fold","hide",function(e,i){var s=t(this),n=e.mode,o="show"===n,a="hide"===n,r=e.size||15,l=/([0-9]+)%/.exec(r),h=!!e.horizFirst,c=h?["right","bottom"]:["bottom","right"],u=e.duration/2,d=t.effects.createPlaceholder(s),p=s.cssClip(),f={clip:t.extend({},p)},g={clip:t.extend({},p)},m=[p[c[0]],p[c[1]]],_=s.queue().length;l&&(r=parseInt(l[1],10)/100*m[a?0:1]),f.clip[c[0]]=r,g.clip[c[0]]=r,g.clip[c[1]]=0,o&&(s.cssClip(g.clip),d&&d.css(t.effects.clipToBox(g)),g.clip=p),s.queue(function(i){d&&d.animate(t.effects.clipToBox(f),u,e.easing).animate(t.effects.clipToBox(g),u,e.easing),i()}).animate(f,u,e.easing).animate(g,u,e.easing).queue(i),t.effects.unshift(s,_,4)}),t.effects.define("highlight","show",function(e,i){var s=t(this),n={backgroundColor:s.css("backgroundColor")};"hide"===e.mode&&(n.opacity=0),t.effects.saveStyle(s),s.css({backgroundImage:"none",backgroundColor:e.color||"#ffff99"}).animate(n,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("size",function(e,i){var s,n,o,a=t(this),r=["fontSize"],l=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],h=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],c=e.mode,u="effect"!==c,d=e.scale||"both",p=e.origin||["middle","center"],f=a.css("position"),g=a.position(),m=t.effects.scaledDimensions(a),_=e.from||m,v=e.to||t.effects.scaledDimensions(a,0);t.effects.createPlaceholder(a),"show"===c&&(o=_,_=v,v=o),n={from:{y:_.height/m.height,x:_.width/m.width},to:{y:v.height/m.height,x:v.width/m.width}},("box"===d||"both"===d)&&(n.from.y!==n.to.y&&(_=t.effects.setTransition(a,l,n.from.y,_),v=t.effects.setTransition(a,l,n.to.y,v)),n.from.x!==n.to.x&&(_=t.effects.setTransition(a,h,n.from.x,_),v=t.effects.setTransition(a,h,n.to.x,v))),("content"===d||"both"===d)&&n.from.y!==n.to.y&&(_=t.effects.setTransition(a,r,n.from.y,_),v=t.effects.setTransition(a,r,n.to.y,v)),p&&(s=t.effects.getBaseline(p,m),_.top=(m.outerHeight-_.outerHeight)*s.y+g.top,_.left=(m.outerWidth-_.outerWidth)*s.x+g.left,v.top=(m.outerHeight-v.outerHeight)*s.y+g.top,v.left=(m.outerWidth-v.outerWidth)*s.x+g.left),a.css(_),("content"===d||"both"===d)&&(l=l.concat(["marginTop","marginBottom"]).concat(r),h=h.concat(["marginLeft","marginRight"]),a.find("*[width]").each(function(){var i=t(this),s=t.effects.scaledDimensions(i),o={height:s.height*n.from.y,width:s.width*n.from.x,outerHeight:s.outerHeight*n.from.y,outerWidth:s.outerWidth*n.from.x},a={height:s.height*n.to.y,width:s.width*n.to.x,outerHeight:s.height*n.to.y,outerWidth:s.width*n.to.x};n.from.y!==n.to.y&&(o=t.effects.setTransition(i,l,n.from.y,o),a=t.effects.setTransition(i,l,n.to.y,a)),n.from.x!==n.to.x&&(o=t.effects.setTransition(i,h,n.from.x,o),a=t.effects.setTransition(i,h,n.to.x,a)),u&&t.effects.saveStyle(i),i.css(o),i.animate(a,e.duration,e.easing,function(){u&&t.effects.restoreStyle(i)})})),a.animate(v,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){var e=a.offset();0===v.opacity&&a.css("opacity",_.opacity),u||(a.css("position","static"===f?"relative":f).offset(e),t.effects.saveStyle(a)),i()}})}),t.effects.define("scale",function(e,i){var s=t(this),n=e.mode,o=parseInt(e.percent,10)||(0===parseInt(e.percent,10)?0:"effect"!==n?0:100),a=t.extend(!0,{from:t.effects.scaledDimensions(s),to:t.effects.scaledDimensions(s,o,e.direction||"both"),origin:e.origin||["middle","center"]},e);e.fade&&(a.from.opacity=1,a.to.opacity=0),t.effects.effect.size.call(this,a,i)}),t.effects.define("puff","hide",function(e,i){var s=t.extend(!0,{},e,{fade:!0,percent:parseInt(e.percent,10)||150});t.effects.effect.scale.call(this,s,i)}),t.effects.define("pulsate","show",function(e,i){var s=t(this),n=e.mode,o="show"===n,a="hide"===n,r=o||a,l=2*(e.times||5)+(r?1:0),h=e.duration/l,c=0,u=1,d=s.queue().length;for((o||!s.is(":visible"))&&(s.css("opacity",0).show(),c=1);l>u;u++)s.animate({opacity:c},h,e.easing),c=1-c;s.animate({opacity:c},h,e.easing),s.queue(i),t.effects.unshift(s,d,l+1)}),t.effects.define("shake",function(e,i){var s=1,n=t(this),o=e.direction||"left",a=e.distance||20,r=e.times||3,l=2*r+1,h=Math.round(e.duration/l),c="up"===o||"down"===o?"top":"left",u="up"===o||"left"===o,d={},p={},f={},g=n.queue().length;for(t.effects.createPlaceholder(n),d[c]=(u?"-=":"+=")+a,p[c]=(u?"+=":"-=")+2*a,f[c]=(u?"-=":"+=")+2*a,n.animate(d,h,e.easing);r>s;s++)n.animate(p,h,e.easing).animate(f,h,e.easing);n.animate(p,h,e.easing).animate(d,h/2,e.easing).queue(i),t.effects.unshift(n,g,l+1)}),t.effects.define("slide","show",function(e,i){var s,n,o=t(this),a={up:["bottom","top"],down:["top","bottom"],left:["right","left"],right:["left","right"]},r=e.mode,l=e.direction||"left",h="up"===l||"down"===l?"top":"left",c="up"===l||"left"===l,u=e.distance||o["top"===h?"outerHeight":"outerWidth"](!0),d={};t.effects.createPlaceholder(o),s=o.cssClip(),n=o.position()[h],d[h]=(c?-1:1)*u+n,d.clip=o.cssClip(),d.clip[a[l][1]]=d.clip[a[l][0]],"show"===r&&(o.cssClip(d.clip),o.css(h,d[h]),d.clip=s,d[h]=n),o.animate(d,{queue:!1,duration:e.duration,easing:e.easing,complete:i})});var v;t.uiBackCompat!==!1&&(v=t.effects.define("transfer",function(e,i){t(this).transfer(e,i)}))});
\ No newline at end of file
diff --git a/keepassxc-browser/keepassxc-browser.css b/keepassxc-browser/keepassxc-browser.css
index d640627..2a1b2ec 100644
--- a/keepassxc-browser/keepassxc-browser.css
+++ b/keepassxc-browser/keepassxc-browser.css
@@ -1,38 +1,47 @@
-.cip-ui-autocomplete li.cip-ui-menu-item {
+.kpxc .ui-autocomplete li.ui-menu-item {
text-align: left !important;
- font-size: 12px !important;
+ font-size: .9em !important;
font-weight: normal !important;
font-style: normal !important;
font-family: Verdana, Arial, sans-serif !important;
color: #222222 !important;
}
-.ui-dialog-titlebar-close {
+.ui-helper-hidden-accessible {
+ display: none !important;
+}
+
+.kpxc .ui-dialog-titlebar-close {
visibility: hidden !important;
}
-.ui-dialog {
+.kpxc .ui-dialog {
font-size: 12px !important;
}
-.dialog-form .ui-dialog-content .ui-widget-content {
+.kpxc .ui-widget-overlay {
+ opacity: 0.0 !important;
+}
+
+.kpxc .dialog-form .ui-dialog-content .ui-widget-content {
max-height: 80px !important;
}
-.ui-dialog-titlebar {
+.kpxc .ui-dialog-titlebar {
background-color: #3a8233;
color: #fff;
}
-.ui-dialog .ui-dialog-buttonpane {
+.kpxc .ui-dialog .ui-dialog-buttonpane {
text-align: center !important;
}
-.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
+
+.kpxc .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
float: none !important;
}
-.ui-button .ui-button-text .ui-button {
- font-size: .10em !important;
+.kpxc .ui-button .ui-button-text .ui-button {
+ font-size: .10em !important;
}
input.genpw-text {
@@ -44,6 +53,7 @@ input.genpw-text {
border-collapse: separate;
width: 100%;
}
+
.genpw-input-group-addon {
font-size: inherit !important;
background-color: #eee;
@@ -69,10 +79,12 @@ input.genpw-text {
position: absolute;
cursor: pointer;
}
+
.cip-genpw-icon.key {
background: url('chrome-extension://__MSG_@@extension_id__/icons/key.png') right no-repeat;
background-size: contain;
}
+
.cip-genpw-icon.key-moz {
background: url('moz-extension://__MSG_@@extension_id__/icons/key.png') right no-repeat;
background-size: contain;
@@ -90,6 +102,7 @@ input.genpw-text {
left: 0;
z-index: 2147483645;
}
+
.b2c-modal-backdrop:after {
content:'';
position: fixed;
@@ -101,9 +114,11 @@ input.genpw-text {
opacity: 0.8;
filter: alpha(opacity=80);
}
+
#b2c-cipDefine-fields {
z-index: 2147483646;
}
+
#b2c-cipDefine-description {
z-index: 2147483646;
color: #efefef;
@@ -117,6 +132,7 @@ input.genpw-text {
background-color:rgba(255,255,255,0.3);
font-size: 15px;
}
+
#b2c-cipDefine-description div:first-of-type {
margin-top: 0;
padding-top: 0;
@@ -126,6 +142,7 @@ input.genpw-text {
font-weight: bold;
font-size: 160%;
}
+
#b2c-cipDefine-description p {
margin-top: 10px;
padding-top: 10px;
@@ -133,9 +150,11 @@ input.genpw-text {
border-top: 2px solid #666666;
line-height: 110%;
}
+
#b2c-help {
margin-bottom: 3px;
}
+
.b2c-fixed-field {
position: absolute;
border: 2px solid #efefef;
@@ -145,27 +164,30 @@ input.genpw-text {
font-weight: bold;
background-color:rgba(239,239,239,0.4);
}
+
.b2c-fixed-hover-field {
border: 2px solid orange;
background-color:rgba(255,165,239,0.4);
}
+
.b2c-fixed-password-field {
border: 2px solid red;
color: #efefef;
background-color:rgba(255,0,0,0.4);
}
+
.b2c-fixed-username-field {
border: 2px solid limegreen;
color: #efefef;
background-color:rgba(50,205,50,0.4);
}
+
.b2c-fixed-string-field {
border: 2px solid deepskyblue;
color: #efefef;
background-color:rgba(30,144,255,0.4);
}
-
.b2c-input-append {
display: inline-block;
margin-bottom: 10px;
@@ -197,4 +219,4 @@ input.genpw-text {
.b2c-input-append select:focus,
.b2c-input-append .b2c-b2c-uneditable-input:focus {
z-index: 2;
-}
\ No newline at end of file
+}
diff --git a/keepassxc-browser/keepassxc-browser.js b/keepassxc-browser/keepassxc-browser.js
index db1ab30..06a4006 100644
--- a/keepassxc-browser/keepassxc-browser.js
+++ b/keepassxc-browser/keepassxc-browser.js
@@ -83,13 +83,19 @@ var cipAutocomplete = {};
cipAutocomplete.elements = [];
cipAutocomplete.init = function(field) {
- if (field.hasClass('ui-autocomplete-input')) {
- //_f(credentialInputs[i].username).autocomplete('source', autocompleteSource);
+ if (cip.settings.autoFillSingleEntry && cip.credentials.length === 1 && field.hasClass('ui-autocomplete-input')) {
field.autocomplete('destroy');
}
+ let acMenu = jQuery('#kpxc-ac-menu');
+ if (acMenu.length == 0) {
+ jQuery('').appendTo('body');
+ }
+
field
+ .addClass('kpxc')
.autocomplete({
+ appendTo: '#kpxc-ac-menu',
minLength: 0,
source: cipAutocomplete.onSource,
select: cipAutocomplete.onSelect,
@@ -106,12 +112,10 @@ cipAutocomplete.onClick = function() {
};
cipAutocomplete.onOpen = function(event, ui) {
- // NOT BEAUTIFUL!
- // modifies ALL ui-autocomplete menus of class .cip-ui-menu
jQuery('ul.ui-autocomplete.ui-menu').css('z-index', 2147483636);
};
-cipAutocomplete.onSource = function (request, callback) {
+cipAutocomplete.onSource = function(request, callback) {
const matches = jQuery.map(cipAutocomplete.elements, (tag) => {
if (tag.label.toUpperCase().indexOf(request.term.toUpperCase()) === 0) {
return tag;
@@ -120,7 +124,7 @@ cipAutocomplete.onSource = function (request, callback) {
callback(matches);
};
-cipAutocomplete.onSelect = function (e, ui) {
+cipAutocomplete.onSelect = function(e, ui) {
e.preventDefault();
cip.setValueWithChange(jQuery(this), ui.item.value);
const fieldId = cipFields.prepareId(jQuery(this).attr('data-cip-id'));
@@ -242,6 +246,7 @@ cipPassword.createDialog = function() {
$dialog.hide();
jQuery('body').append($dialog);
+
$dialog.dialog({
autoOpen: false,
modal: true,
@@ -309,14 +314,20 @@ cipPassword.createDialog = function() {
}
},
open: function(event, ui) {
+ // Dirty hacks for overlay and custom CSS
+ jQuery('.ui-widget-overlay').wrap('
');
jQuery('.ui-widget-overlay').click(function() {
jQuery('#cip-genpw-dialog:first').dialog('close');
+ jQuery('span').remove('.kpxc');
});
if (jQuery('input#cip-genpw-textfield-password:first').val() === '') {
jQuery('button#cip-genpw-btn-generate:first').click();
}
- }
+ },
+ create: function(event, ui) {
+ jQuery('.ui-dialog').wrap('
');
+ }
});
};
From 002833bd622ecd902b8bbdc1d5e1e058b2dbbeba Mon Sep 17 00:00:00 2001
From: varjolintu
Date: Fri, 17 Nov 2017 20:07:39 +0200
Subject: [PATCH 18/24] Changelog
---
CHANGELOG | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG b/CHANGELOG
index c21b0b8..40de540 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,9 @@
+0.4.1 (??-??-2017)
+=========================
+- Added support for the credentials dropdown menu with only password field visible
+- Fixed jQuery overriding with custom scoped CSS
+- Fixed non-necessary destroying of autocomplete on autofill
+
0.4.0 (13-11-2017)
=========================
- Fixed showing context menu on password fields with Firefox
From 124204a5c1fb39450ffb490e30af0e75d341e2f6 Mon Sep 17 00:00:00 2001
From: varjolintu
Date: Sat, 18 Nov 2017 08:08:01 +0200
Subject: [PATCH 19/24] Changelog
---
CHANGELOG | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG b/CHANGELOG
index 40de540..320892e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,4 @@
-0.4.1 (??-??-2017)
+0.4.1 (18-11-2017)
=========================
- Added support for the credentials dropdown menu with only password field visible
- Fixed jQuery overriding with custom scoped CSS
From cf2d068ed7e4ed4cef6faf54e7ccdb1ab09116e5 Mon Sep 17 00:00:00 2001
From: varjolintu
Date: Thu, 23 Nov 2017 18:02:07 +0200
Subject: [PATCH 20/24] Fixed HTTP auth, decrypt error handling and
database-locked response handling
---
CHANGELOG | 7 +
README.md | 1 +
keepassxc-browser/background/httpauth.js | 10 +-
keepassxc-browser/background/keepass.js | 233 ++++++++++++---------
keepassxc-browser/background/page.js | 2 +-
keepassxc-browser/manifest.json | 2 +-
keepassxc-browser/popups/popup_httpauth.js | 20 +-
7 files changed, 159 insertions(+), 116 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index 320892e..979cd36 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,10 @@
+0.4.2 (??-??-2017)
+=========================
+- Fixed HTTP authentication with multiple credentials (credits to smorks)
+- Fixed error handling when decrypt fails
+- Fixed database-locked response handling
+- TODO: Fixed nonce increment when encrypting messages
+
0.4.1 (18-11-2017)
=========================
- Added support for the credentials dropdown menu with only password field visible
diff --git a/README.md b/README.md
index 04a9c7b..6b04489 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@ The following improvements and features have been made after the fork. At this p
- New buttons, icons and settings page graphics
- Redesigned password generator dialog
- Password generator supports diceware passphrases and extended ASCII characters
+- Autocomplete works also when only password fields are visible
## Protocol
diff --git a/keepassxc-browser/background/httpauth.js b/keepassxc-browser/background/httpauth.js
index 35dc99e..f74277d 100644
--- a/keepassxc-browser/background/httpauth.js
+++ b/keepassxc-browser/background/httpauth.js
@@ -5,7 +5,7 @@ httpAuth.pendingCallbacks = [];
httpAuth.requestCompleted = function(details) {
let index = httpAuth.requests.indexOf(details.requestId);
- if (index > -1) {
+ if (index >= 0) {
httpAuth.requests.splice(index, 1);
}
};
@@ -23,6 +23,7 @@ httpAuth.handleRequestCallback = function(details, callback) {
httpAuth.processPendingCallbacks = function(details, resolve, reject) {
if (httpAuth.requests.indexOf(details.requestId) >= 0 || !page.tabs[details.tabId]) {
reject({});
+ return;
}
httpAuth.requests.push(details.requestId);
@@ -41,10 +42,7 @@ httpAuth.processPendingCallbacks = function(details, resolve, reject) {
httpAuth.loginOrShowCredentials = function(logins, details, resolve, reject) {
// at least one login found --> use first to login
if (logins.length > 0) {
- kpxcEvent.onHTTPAuthPopup(null, { "id": details.tabId }, { "logins": logins, "url": details.searchUrl });
- //generate popup-list for HTTP Auth usernames + descriptions
-
- if (page.settings.autoFillAndSend) {
+ if (logins.length == 1 && page.settings.autoFillAndSend) {
resolve({
authCredentials: {
username: logins[0].login,
@@ -52,7 +50,7 @@ httpAuth.loginOrShowCredentials = function(logins, details, resolve, reject) {
}
});
} else {
- reject({});
+ kpxcEvent.onHTTPAuthPopup(null, { 'id': details.tabId }, { 'logins': logins, 'url': details.searchUrl, 'resolve': resolve });
}
}
// no logins found
diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js
index 2a5665d..55b52ca 100644
--- a/keepassxc-browser/background/keepass.js
+++ b/keepassxc-browser/background/keepass.js
@@ -167,11 +167,15 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
- if (res) {
- const message = nacl.util.encodeUTF8(res);
- const parsed = JSON.parse(message);
- callback(keepass.verifyResponse(parsed, response.nonce) ? 'success' : 'error');
+ if (!res) {
+ keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
+ callback('error');
+ return;
}
+
+ const message = nacl.util.encodeUTF8(res);
+ const parsed = JSON.parse(message);
+ callback(keepass.verifyResponse(parsed, response.nonce) ? 'success' : 'error');
}
else if (response.error && response.errorCode) {
keepass.handleError(tab, response.errorCode, response.error);
@@ -228,25 +232,29 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
- if (res) {
- const message = nacl.util.encodeUTF8(res);
- const parsed = JSON.parse(message);
- keepass.setcurrentKeePassXCVersion(parsed.version);
-
- if (keepass.verifyResponse(parsed, response.nonce)) {
- entries = parsed.entries;
- keepass.updateLastUsed(keepass.databaseHash);
- if (entries.length === 0) {
- // questionmark-icon is not triggered, so we have to trigger for the normal symbol
- browserAction.showDefault(null, tab);
- }
- callback(entries);
- }
- else {
- console.log('RetrieveCredentials for ' + url + ' rejected');
- }
- page.debug('keepass.retrieveCredentials() => entries.length = {1}', entries.length);
+ if (!res) {
+ keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
+ callback([]);
+ return;
}
+
+ const message = nacl.util.encodeUTF8(res);
+ const parsed = JSON.parse(message);
+ keepass.setcurrentKeePassXCVersion(parsed.version);
+
+ if (keepass.verifyResponse(parsed, response.nonce)) {
+ entries = parsed.entries;
+ keepass.updateLastUsed(keepass.databaseHash);
+ if (entries.length === 0) {
+ // questionmark-icon is not triggered, so we have to trigger for the normal symbol
+ browserAction.showDefault(null, tab);
+ }
+ callback(entries);
+ }
+ else {
+ console.log('RetrieveCredentials for ' + url + ' rejected');
+ }
+ page.debug('keepass.retrieveCredentials() => entries.length = {1}', entries.length);
}
else if (response.error && response.errorCode) {
keepass.handleError(tab, response.errorCode, response.error);
@@ -292,25 +300,29 @@ keepass.generatePassword = function(callback, tab, forceCallback) {
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
- if (res) {
- const message = nacl.util.encodeUTF8(res);
- const parsed = JSON.parse(message);
- keepass.setcurrentKeePassXCVersion(parsed.version);
+ if (!res) {
+ keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
+ callback([]);
+ return;
+ }
- if (keepass.verifyResponse(parsed, response.nonce)) {
- if (parsed.entries) {
- passwords = parsed.entries;
- keepass.updateLastUsed(keepass.databaseHash);
- }
- else {
- console.log('No entries returned. Is KeePassXC up-to-date?');
- }
+ const message = nacl.util.encodeUTF8(res);
+ const parsed = JSON.parse(message);
+ keepass.setcurrentKeePassXCVersion(parsed.version);
+
+ if (keepass.verifyResponse(parsed, response.nonce)) {
+ if (parsed.entries) {
+ passwords = parsed.entries;
+ keepass.updateLastUsed(keepass.databaseHash);
}
else {
- console.log('GeneratePassword rejected');
+ console.log('No entries returned. Is KeePassXC up-to-date?');
}
- callback(passwords);
}
+ else {
+ console.log('GeneratePassword rejected');
+ }
+ callback(passwords);
}
else if (response.error && response.errorCode) {
keepass.handleError(tab, response.errorCode, response.error);
@@ -352,23 +364,26 @@ keepass.associate = function(callback, tab) {
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
- if (res) {
- const message = nacl.util.encodeUTF8(res);
- const parsed = JSON.parse(message);
- keepass.setcurrentKeePassXCVersion(parsed.version);
- const id = parsed.id;
-
- if (!keepass.verifyResponse(parsed, response.nonce)) {
- keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED);
- }
- else {
- keepass.setCryptoKey(id, key); // Save the current public key as id key for the database
- keepass.associated.value = true;
- keepass.associated.hash = parsed.hash || 0;
- }
-
- browserAction.show(callback, tab);
+ if (!res) {
+ keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
+ return;
}
+
+ const message = nacl.util.encodeUTF8(res);
+ const parsed = JSON.parse(message);
+ keepass.setcurrentKeePassXCVersion(parsed.version);
+ const id = parsed.id;
+
+ if (!keepass.verifyResponse(parsed, response.nonce)) {
+ keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED);
+ }
+ else {
+ keepass.setCryptoKey(id, key); // Save the current public key as id key for the database
+ keepass.associated.value = true;
+ keepass.associated.hash = parsed.hash || 0;
+ }
+
+ browserAction.show(callback, tab);
}
else if (response.error && response.errorCode) {
keepass.handleError(tab, response.errorCode, response.error);
@@ -434,27 +449,31 @@ keepass.testAssociation = function(callback, tab, enableTimeout = false) {
keepass.sendNativeMessage(request, enableTimeout).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
- if (res) {
- const message = nacl.util.encodeUTF8(res);
- const parsed = JSON.parse(message);
- keepass.setcurrentKeePassXCVersion(parsed.version);
- keepass.isEncryptionKeyUnrecognized = false;
+ if (!res) {
+ keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
+ callback(false);
+ return;
+ }
- if (!keepass.verifyResponse(parsed, response.nonce)) {
- const hash = response.hash || 0;
- keepass.deleteKey(hash);
- keepass.isEncryptionKeyUnrecognized = true;
- keepass.handleError(tab, kpErrors.ENCRYPTION_KEY_UNRECOGNIZED);
- keepass.associated.value = false;
- keepass.associated.hash = null;
- }
- else if (!keepass.isAssociated()) {
- keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED);
- }
- else {
- if (tab && page.tabs[tab.id]) {
- delete page.tabs[tab.id].errorMessage;
- }
+ const message = nacl.util.encodeUTF8(res);
+ const parsed = JSON.parse(message);
+ keepass.setcurrentKeePassXCVersion(parsed.version);
+ keepass.isEncryptionKeyUnrecognized = false;
+
+ if (!keepass.verifyResponse(parsed, response.nonce)) {
+ const hash = response.hash || 0;
+ keepass.deleteKey(hash);
+ keepass.isEncryptionKeyUnrecognized = true;
+ keepass.handleError(tab, kpErrors.ENCRYPTION_KEY_UNRECOGNIZED);
+ keepass.associated.value = false;
+ keepass.associated.hash = null;
+ }
+ else if (!keepass.isAssociated()) {
+ keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED);
+ }
+ else {
+ if (tab && page.tabs[tab.id]) {
+ delete page.tabs[tab.id].errorMessage;
}
}
}
@@ -501,30 +520,34 @@ keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) {
keepass.sendNativeMessage(request, enableTimeout).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
- if (res) {
- const message = nacl.util.encodeUTF8(res);
- const parsed = JSON.parse(message);
+ if (!res) {
+ keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
+ callback('no-hash');
+ return;
+ }
+
+ const message = nacl.util.encodeUTF8(res);
+ const parsed = JSON.parse(message);
- if (parsed.hash) {
- const oldDatabaseHash = keepass.databaseHash;
- keepass.setcurrentKeePassXCVersion(parsed.version);
- keepass.databaseHash = parsed.hash || 'no-hash';
+ if (parsed.hash) {
+ const oldDatabaseHash = keepass.databaseHash;
+ keepass.setcurrentKeePassXCVersion(parsed.version);
+ keepass.databaseHash = parsed.hash || 'no-hash';
- if (oldDatabaseHash && oldDatabaseHash != keepass.databaseHash) {
- keepass.associated.value = false;
- keepass.associated.hash = null;
- }
-
- keepass.isDatabaseClosed = false;
- keepass.isKeePassXCAvailable = true;
- callback(parsed.hash);
- }
- else if (parsed.errorCode) {
- keepass.databaseHash = 'no-hash';
- keepass.isDatabaseClosed = true;
- keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED);
- callback(keepass.databaseHash);
+ if (oldDatabaseHash && oldDatabaseHash != keepass.databaseHash) {
+ keepass.associated.value = false;
+ keepass.associated.hash = null;
}
+
+ keepass.isDatabaseClosed = false;
+ keepass.isKeePassXCAvailable = true;
+ callback(parsed.hash);
+ }
+ else if (parsed.errorCode) {
+ keepass.databaseHash = 'no-hash';
+ keepass.isDatabaseClosed = true;
+ keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED);
+ callback(keepass.databaseHash);
}
}
else {
@@ -604,16 +627,22 @@ keepass.lockDatabase = function(tab) {
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
- if (res) {
- const message = nacl.util.encodeUTF8(res);
- const parsed = JSON.parse(message);
- keepass.setcurrentKeePassXCVersion(parsed.version);
+ if (!res) {
+ keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
+ resolve(false);
+ return;
+ }
- if (keepass.verifyResponse(parsed, response.nonce)) {
- keepass.isDatabaseClosed = true;
- keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED);
- resolve(false);
- }
+ const message = nacl.util.encodeUTF8(res);
+ const parsed = JSON.parse(message);
+ keepass.setcurrentKeePassXCVersion(parsed.version);
+
+ if (keepass.verifyResponse(parsed, response.nonce)) {
+ keepass.isDatabaseClosed = true;
+
+ // Display error message in the popup
+ keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED);
+ resolve(true);
}
}
else if (response.error && response.errorCode) {
diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js
index 5b9c525..8a9d5c2 100644
--- a/keepassxc-browser/background/page.js
+++ b/keepassxc-browser/background/page.js
@@ -48,7 +48,7 @@ page.initOpenedTabs = function() {
}
// set initial tab-ID
- browser.tabs.query({ "active": true, "currentWindow": true }).then((tabs) => {
+ browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => {
if (tabs.length === 0) {
resolve();
return; // For example: only the background devtools or a popup are opened
diff --git a/keepassxc-browser/manifest.json b/keepassxc-browser/manifest.json
index 98a1b1a..ae67e9e 100644
--- a/keepassxc-browser/manifest.json
+++ b/keepassxc-browser/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "keepassxc-browser",
- "version": "0.4.1",
+ "version": "0.4.2",
"description": "KeePassXC integration for modern web browsers",
"author": "Sami Vänttinen",
"icons": {
diff --git a/keepassxc-browser/popups/popup_httpauth.js b/keepassxc-browser/popups/popup_httpauth.js
index a76a279..7aca550 100644
--- a/keepassxc-browser/popups/popup_httpauth.js
+++ b/keepassxc-browser/popups/popup_httpauth.js
@@ -1,17 +1,25 @@
$(function() {
browser.runtime.getBackgroundPage().then((global) => {
- browser.tabs.query({"active": true, "currentWindow": true}).then((tab) => {
+ browser.tabs.query({'active': true, 'currentWindow': true}).then((tabs) => {
+ let tab = tabs[0];
const data = global.page.tabs[tab.id].loginList;
let ul = document.getElementById('login-list');
for (let i = 0; i < data.logins.length; i++) {
const li = document.createElement('li');
const a = document.createElement('a');
- a.textContent = data.logins[i].login + ' (' + data.logins[i].name + ')';
- li.setAttribute('class', 'list-group-item');
+ a.textContent = data.logins[i].login + " (" + data.logins[i].name + ")";
li.appendChild(a);
- $(a).data('url', data.url.replace(/:\/\//g, '://' + data.logins[i].login + ':' + data.logins[i].password + '@'));
- $(a).click(() => {
- browser.tabs.update(tab.id, {'url': $(this).data('url')});
+ $(a).data('creds', data.logins[i]);
+ $(a).click(function () {
+ if (data.resolve) {
+ const creds = $(this).data('creds');
+ data.resolve({
+ authCredentials: {
+ username: creds.login,
+ password: creds.password
+ }
+ });
+ }
close();
});
ul.appendChild(li);
From 84642ffc553a86234d72307cd18d801ab2386e7d Mon Sep 17 00:00:00 2001
From: varjolintu
Date: Fri, 24 Nov 2017 14:57:36 +0200
Subject: [PATCH 21/24] Nonce incrementation part1
---
keepassxc-browser/background/keepass.js | 119 +++++++++++++++++-------
1 file changed, 84 insertions(+), 35 deletions(-)
diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js
index 55b52ca..ddda312 100644
--- a/keepassxc-browser/background/keepass.js
+++ b/keepassxc-browser/background/keepass.js
@@ -21,6 +21,7 @@ keepass.databaseHash = 'no-hash'; //no-hash = KeePassXC is too old and does not
keepass.keyId = 'keepassxc-browser-cryptokey-name';
keepass.keyBody = 'keepassxc-browser-key';
keepass.messageTimeout = 500; // milliseconds
+keepass.nonce = nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize));
const kpActions = {
SET_LOGIN: 'set-login',
@@ -142,7 +143,7 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
const kpAction = kpActions.SET_LOGIN;
const {dbid} = keepass.getCryptoKey();
- const nonce = nacl.randomBytes(keepass.keySize);
+ const nonce = keepass.getNonce();
let messageData = {
action: kpAction,
@@ -160,10 +161,10 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
const request = {
action: kpAction,
message: keepass.encrypt(messageData, nonce),
- nonce: nacl.util.encodeBase64(nonce),
+ nonce: nonce,
clientID: keepass.clientID
};
-
+ console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -175,7 +176,7 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
const message = nacl.util.encodeUTF8(res);
const parsed = JSON.parse(message);
- callback(keepass.verifyResponse(parsed, response.nonce) ? 'success' : 'error');
+ callback(keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce)) ? 'success' : 'error');
}
else if (response.error && response.errorCode) {
keepass.handleError(tab, response.errorCode, response.error);
@@ -209,7 +210,7 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
let entries = [];
const kpAction = kpActions.GET_LOGINS;
- const nonce = nacl.randomBytes(keepass.keySize);
+ const nonce = keepass.getNonce();
const {dbid} = keepass.getCryptoKey();
let messageData = {
@@ -225,10 +226,10 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
const request = {
action: kpAction,
message: keepass.encrypt(messageData, nonce),
- nonce: nacl.util.encodeBase64(nonce),
+ nonce: nonce,
clientID: keepass.clientID
};
-
+ console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -242,7 +243,7 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
const parsed = JSON.parse(message);
keepass.setcurrentKeePassXCVersion(parsed.version);
- if (keepass.verifyResponse(parsed, response.nonce)) {
+ if (keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
entries = parsed.entries;
keepass.updateLastUsed(keepass.databaseHash);
if (entries.length === 0) {
@@ -289,14 +290,14 @@ keepass.generatePassword = function(callback, tab, forceCallback) {
let passwords = [];
const kpAction = kpActions.GENERATE_PASSWORD;
- const nonce = nacl.randomBytes(keepass.keySize);
+ const nonce = keepass.getNonce();
const request = {
action: kpAction,
- nonce: nacl.util.encodeBase64(nonce),
+ nonce: nonce,
clientID: keepass.clientID
};
-
+ console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -310,7 +311,7 @@ keepass.generatePassword = function(callback, tab, forceCallback) {
const parsed = JSON.parse(message);
keepass.setcurrentKeePassXCVersion(parsed.version);
- if (keepass.verifyResponse(parsed, response.nonce)) {
+ if (keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
if (parsed.entries) {
passwords = parsed.entries;
keepass.updateLastUsed(keepass.databaseHash);
@@ -347,7 +348,7 @@ keepass.associate = function(callback, tab) {
const kpAction = kpActions.ASSOCIATE;
const key = nacl.util.encodeBase64(keepass.keyPair.publicKey);
- const nonce = nacl.randomBytes(keepass.keySize);
+ const nonce = keepass.getNonce();
const messageData = {
action: kpAction,
@@ -357,10 +358,10 @@ keepass.associate = function(callback, tab) {
const request = {
action: kpAction,
message: keepass.encrypt(messageData, nonce),
- nonce: nacl.util.encodeBase64(nonce),
+ nonce: nonce,
clientID: keepass.clientID
};
-
+ console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -374,7 +375,7 @@ keepass.associate = function(callback, tab) {
keepass.setcurrentKeePassXCVersion(parsed.version);
const id = parsed.id;
- if (!keepass.verifyResponse(parsed, response.nonce)) {
+ if (!keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED);
}
else {
@@ -422,7 +423,7 @@ keepass.testAssociation = function(callback, tab, enableTimeout = false) {
}
const kpAction = kpActions.TEST_ASSOCIATE;
- const nonce = nacl.randomBytes(keepass.keySize);
+ const nonce = keepass.getNonce();
const {dbid, dbkey} = keepass.getCryptoKey();
if (dbkey === null || dbid === null) {
@@ -442,10 +443,10 @@ keepass.testAssociation = function(callback, tab, enableTimeout = false) {
const request = {
action: kpAction,
message: keepass.encrypt(messageData, nonce),
- nonce: nacl.util.encodeBase64(nonce),
+ nonce: nonce,
clientID: keepass.clientID
};
-
+ console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
keepass.sendNativeMessage(request, enableTimeout).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -460,7 +461,7 @@ keepass.testAssociation = function(callback, tab, enableTimeout = false) {
keepass.setcurrentKeePassXCVersion(parsed.version);
keepass.isEncryptionKeyUnrecognized = false;
- if (!keepass.verifyResponse(parsed, response.nonce)) {
+ if (!keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
const hash = response.hash || 0;
keepass.deleteKey(hash);
keepass.isEncryptionKeyUnrecognized = true;
@@ -497,7 +498,7 @@ keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) {
}
const kpAction = kpActions.GET_DATABASE_HASH;
- const nonce = nacl.randomBytes(keepass.keySize);
+ const nonce = keepass.getNonce();
const messageData = {
action: kpAction
@@ -513,10 +514,10 @@ keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) {
const request = {
action: kpAction,
message: encrypted,
- nonce: nacl.util.encodeBase64(nonce),
+ nonce: nonce,
clientID: keepass.clientID
};
-
+ console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
keepass.sendNativeMessage(request, enableTimeout).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -525,7 +526,7 @@ keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) {
callback('no-hash');
return;
}
-
+
const message = nacl.util.encodeUTF8(res);
const parsed = JSON.parse(message);
@@ -574,8 +575,7 @@ keepass.changePublicKeys = function(tab, enableTimeout = false) {
const kpAction = kpActions.CHANGE_PUBLIC_KEYS;
const key = nacl.util.encodeBase64(keepass.keyPair.publicKey);
- let nonce = nacl.randomBytes(keepass.keySize);
- nonce = nacl.util.encodeBase64(nonce);
+ const nonce = keepass.getNonce();
keepass.clientID = nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize));
const request = {
@@ -584,11 +584,11 @@ keepass.changePublicKeys = function(tab, enableTimeout = false) {
nonce: nonce,
clientID: keepass.clientID
};
-
+ console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
keepass.sendNativeMessage(request, enableTimeout).then((response) => {
keepass.setcurrentKeePassXCVersion(response.version);
- if (!keepass.verifyKeyResponse(response, key, nonce)) {
+ if (!keepass.verifyKeyResponse(response, key, keepass.incrementedNonce(nonce))) {
if (tab && page.tabs[tab.id]) {
keepass.handleError(tab, kpErrors.KEY_CHANGE_FAILED);
reject(false);
@@ -611,7 +611,7 @@ keepass.lockDatabase = function(tab) {
}
const kpAction = kpActions.LOCK_DATABASE;
- const nonce = nacl.randomBytes(keepass.keySize);
+ const nonce = keepass.getNonce();
const messageData = {
action: kpAction
@@ -620,10 +620,10 @@ keepass.lockDatabase = function(tab) {
const request = {
action: kpAction,
message: keepass.encrypt(messageData, nonce),
- nonce: nacl.util.encodeBase64(nonce),
+ nonce: nonce,
clientID: keepass.clientID
};
-
+ console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -631,13 +631,13 @@ keepass.lockDatabase = function(tab) {
keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
resolve(false);
return;
- }
+ }
const message = nacl.util.encodeUTF8(res);
const parsed = JSON.parse(message);
keepass.setcurrentKeePassXCVersion(parsed.version);
- if (keepass.verifyResponse(parsed, response.nonce)) {
+ if (keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
keepass.isDatabaseClosed = true;
// Display error message in the popup
@@ -824,6 +824,51 @@ function onDisconnected() {
console.log('Failed to connect: ' + (browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError.message));
}
+keepass.getNonce = function() {
+ return nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize));
+
+ // New implementation
+ const oldNonce = nacl.util.decodeBase64(keepass.nonce);
+
+ let newNonce = [];
+ for (let i = 0; i < 24; i++){
+ newNonce[i] = oldNonce[i];
+ }
+
+ for (let i = 0; i < 24; i++) {
+ newNonce[i]++;
+ if (newNonce[i]) {
+ break;
+ }
+ }
+
+ console.log("New: " + newNonce);
+ console.log("Old: " + oldNonce);
+ keepass.nonce = nacl.util.encodeBase64(newNonce);
+ return nacl.util.encodeBase64(oldNonce);
+};
+
+keepass.incrementedNonce = function(nonce) {
+ const oldNonce = nacl.util.decodeBase64(nonce);
+
+ // TODO: fix the incrementation, it's not complete yet
+ let newNonce = [];
+ for (let i = 0; i < 24; i++){
+ newNonce[i] = oldNonce[i];
+ }
+
+ for (let i = 0; i < 24; i++) {
+ newNonce[i]++;
+ if (newNonce[i]) {
+ break;
+ }
+ }
+
+ console.log("New: " + newNonce);
+ console.log("Old: " + oldNonce);
+ return nacl.util.encodeBase64(newNonce);
+};
+
keepass.nativeConnect = function() {
console.log('Connecting to native messaging host ' + keepass.nativeHostName);
keepass.nativePort = browser.runtime.connectNative(keepass.nativeHostName);
@@ -867,6 +912,9 @@ keepass.verifyResponse = function(response, nonce, id) {
}
keepass.associated.value = (response.nonce === nonce);
+ if (keepass.associated.value === false) {
+ console.log("Compare failed");
+ }
if (id) {
keepass.associated.value = (keepass.associated.value && id === response.id);
@@ -909,9 +957,10 @@ keepass.setCryptoKey = function(id, key) {
keepass.encrypt = function(input, nonce) {
const messageData = nacl.util.decodeUTF8(JSON.stringify(input));
+ const messageNonce = nacl.util.decodeBase64(nonce);
if (keepass.serverPublicKey) {
- const message = nacl.box(messageData, nonce, keepass.serverPublicKey, keepass.keyPair.secretKey);
+ const message = nacl.box(messageData, messageNonce, keepass.serverPublicKey, keepass.keyPair.secretKey);
if (message) {
return nacl.util.encodeBase64(message);
}
@@ -919,7 +968,7 @@ keepass.encrypt = function(input, nonce) {
return '';
};
-keepass.decrypt = function(input, nonce, toStr) {
+keepass.decrypt = function(input, nonce) {
const m = nacl.util.decodeBase64(input);
const n = nacl.util.decodeBase64(nonce);
const res = nacl.box.open(m, n, keepass.serverPublicKey, keepass.keyPair.secretKey);
From c491356dc9094b049dcd0ac0ce82770145027f35 Mon Sep 17 00:00:00 2001
From: varjolintu
Date: Sun, 26 Nov 2017 12:31:05 +0200
Subject: [PATCH 22/24] Nonce incrementation
---
CHANGELOG | 2 +-
keepassxc-browser/background/keepass.js | 92 ++++++++++---------------
keepassxc-protocol.md | 2 +-
3 files changed, 39 insertions(+), 57 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index 979cd36..afe4ec6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,7 +3,7 @@
- Fixed HTTP authentication with multiple credentials (credits to smorks)
- Fixed error handling when decrypt fails
- Fixed database-locked response handling
-- TODO: Fixed nonce increment when encrypting messages
+- Fixed nonce increment when encrypting messages
0.4.1 (18-11-2017)
=========================
diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js
index ddda312..ae435ac 100644
--- a/keepassxc-browser/background/keepass.js
+++ b/keepassxc-browser/background/keepass.js
@@ -134,8 +134,7 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
page.tabs[tab.id].errorMessage = null;
keepass.testAssociation((response) => {
- if (!response)
- {
+ if (!response) {
browserAction.showDefault(null, tab);
callback([]);
return;
@@ -144,6 +143,7 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
const kpAction = kpActions.SET_LOGIN;
const {dbid} = keepass.getCryptoKey();
const nonce = keepass.getNonce();
+ const incrementedNonce = keepass.incrementedNonce(nonce);
let messageData = {
action: kpAction,
@@ -164,7 +164,7 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
nonce: nonce,
clientID: keepass.clientID
};
- console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
+
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -176,7 +176,7 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
const message = nacl.util.encodeUTF8(res);
const parsed = JSON.parse(message);
- callback(keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce)) ? 'success' : 'error');
+ callback(keepass.verifyResponse(parsed, incrementedNonce) ? 'success' : 'error');
}
else if (response.error && response.errorCode) {
keepass.handleError(tab, response.errorCode, response.error);
@@ -192,8 +192,7 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
page.debug('keepass.retrieveCredentials(callback, {1}, {2}, {3}, {4})', tab.id, url, submiturl, forceCallback);
keepass.testAssociation((response) => {
- if (!response)
- {
+ if (!response) {
browserAction.showDefault(null, tab);
if (forceCallback) {
callback([]);
@@ -211,6 +210,7 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
let entries = [];
const kpAction = kpActions.GET_LOGINS;
const nonce = keepass.getNonce();
+ const incrementedNonce = keepass.incrementedNonce(nonce);
const {dbid} = keepass.getCryptoKey();
let messageData = {
@@ -229,7 +229,7 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
nonce: nonce,
clientID: keepass.clientID
};
- console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
+
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -243,7 +243,7 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
const parsed = JSON.parse(message);
keepass.setcurrentKeePassXCVersion(parsed.version);
- if (keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
+ if (keepass.verifyResponse(parsed, incrementedNonce)) {
entries = parsed.entries;
keepass.updateLastUsed(keepass.databaseHash);
if (entries.length === 0) {
@@ -274,8 +274,7 @@ keepass.generatePassword = function(callback, tab, forceCallback) {
}
keepass.testAssociation((taresponse) => {
- if (!taresponse)
- {
+ if (!taresponse) {
browserAction.showDefault(null, tab);
if (forceCallback) {
callback([]);
@@ -291,13 +290,14 @@ keepass.generatePassword = function(callback, tab, forceCallback) {
let passwords = [];
const kpAction = kpActions.GENERATE_PASSWORD;
const nonce = keepass.getNonce();
+ const incrementedNonce = keepass.incrementedNonce(nonce);
const request = {
action: kpAction,
nonce: nonce,
clientID: keepass.clientID
};
- console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
+
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -311,7 +311,7 @@ keepass.generatePassword = function(callback, tab, forceCallback) {
const parsed = JSON.parse(message);
keepass.setcurrentKeePassXCVersion(parsed.version);
- if (keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
+ if (keepass.verifyResponse(parsed, incrementedNonce)) {
if (parsed.entries) {
passwords = parsed.entries;
keepass.updateLastUsed(keepass.databaseHash);
@@ -349,6 +349,7 @@ keepass.associate = function(callback, tab) {
const kpAction = kpActions.ASSOCIATE;
const key = nacl.util.encodeBase64(keepass.keyPair.publicKey);
const nonce = keepass.getNonce();
+ const incrementedNonce = keepass.incrementedNonce(nonce);
const messageData = {
action: kpAction,
@@ -361,7 +362,7 @@ keepass.associate = function(callback, tab) {
nonce: nonce,
clientID: keepass.clientID
};
- console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
+
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -375,7 +376,7 @@ keepass.associate = function(callback, tab) {
keepass.setcurrentKeePassXCVersion(parsed.version);
const id = parsed.id;
- if (!keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
+ if (!keepass.verifyResponse(parsed, incrementedNonce)) {
keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED);
}
else {
@@ -424,6 +425,7 @@ keepass.testAssociation = function(callback, tab, enableTimeout = false) {
const kpAction = kpActions.TEST_ASSOCIATE;
const nonce = keepass.getNonce();
+ const incrementedNonce = keepass.incrementedNonce(nonce);
const {dbid, dbkey} = keepass.getCryptoKey();
if (dbkey === null || dbid === null) {
@@ -446,7 +448,7 @@ keepass.testAssociation = function(callback, tab, enableTimeout = false) {
nonce: nonce,
clientID: keepass.clientID
};
- console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
+
keepass.sendNativeMessage(request, enableTimeout).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -461,7 +463,7 @@ keepass.testAssociation = function(callback, tab, enableTimeout = false) {
keepass.setcurrentKeePassXCVersion(parsed.version);
keepass.isEncryptionKeyUnrecognized = false;
- if (!keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
+ if (!keepass.verifyResponse(parsed, incrementedNonce)) {
const hash = response.hash || 0;
keepass.deleteKey(hash);
keepass.isEncryptionKeyUnrecognized = true;
@@ -499,6 +501,7 @@ keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) {
const kpAction = kpActions.GET_DATABASE_HASH;
const nonce = keepass.getNonce();
+ const incrementedNonce = keepass.incrementedNonce(nonce);
const messageData = {
action: kpAction
@@ -517,7 +520,7 @@ keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) {
nonce: nonce,
clientID: keepass.clientID
};
- console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
+
keepass.sendNativeMessage(request, enableTimeout).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -529,7 +532,6 @@ keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) {
const message = nacl.util.encodeUTF8(res);
const parsed = JSON.parse(message);
-
if (parsed.hash) {
const oldDatabaseHash = keepass.databaseHash;
keepass.setcurrentKeePassXCVersion(parsed.version);
@@ -543,12 +545,14 @@ keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) {
keepass.isDatabaseClosed = false;
keepass.isKeePassXCAvailable = true;
callback(parsed.hash);
+ return;
}
else if (parsed.errorCode) {
keepass.databaseHash = 'no-hash';
keepass.isDatabaseClosed = true;
keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED);
callback(keepass.databaseHash);
+ return;
}
}
else {
@@ -562,6 +566,7 @@ keepass.getDatabaseHash = function(callback, tab, enableTimeout = false) {
keepass.handleError(tab, response.errorCode, response.error);
}
callback(keepass.databaseHash);
+ return;
}
});
};
@@ -576,6 +581,7 @@ keepass.changePublicKeys = function(tab, enableTimeout = false) {
const kpAction = kpActions.CHANGE_PUBLIC_KEYS;
const key = nacl.util.encodeBase64(keepass.keyPair.publicKey);
const nonce = keepass.getNonce();
+ const incrementedNonce = keepass.incrementedNonce(nonce);
keepass.clientID = nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize));
const request = {
@@ -584,11 +590,11 @@ keepass.changePublicKeys = function(tab, enableTimeout = false) {
nonce: nonce,
clientID: keepass.clientID
};
- console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
+
keepass.sendNativeMessage(request, enableTimeout).then((response) => {
keepass.setcurrentKeePassXCVersion(response.version);
- if (!keepass.verifyKeyResponse(response, key, keepass.incrementedNonce(nonce))) {
+ if (!keepass.verifyKeyResponse(response, key, incrementedNonce)) {
if (tab && page.tabs[tab.id]) {
keepass.handleError(tab, kpErrors.KEY_CHANGE_FAILED);
reject(false);
@@ -612,6 +618,7 @@ keepass.lockDatabase = function(tab) {
const kpAction = kpActions.LOCK_DATABASE;
const nonce = keepass.getNonce();
+ const incrementedNonce = keepass.incrementedNonce(nonce);
const messageData = {
action: kpAction
@@ -623,7 +630,7 @@ keepass.lockDatabase = function(tab) {
nonce: nonce,
clientID: keepass.clientID
};
- console.log(kpAction + " " + nacl.util.decodeBase64(nonce));
+
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
@@ -637,7 +644,7 @@ keepass.lockDatabase = function(tab) {
const parsed = JSON.parse(message);
keepass.setcurrentKeePassXCVersion(parsed.version);
- if (keepass.verifyResponse(parsed, keepass.incrementedNonce(nonce))) {
+ if (keepass.verifyResponse(parsed, incrementedNonce)) {
keepass.isDatabaseClosed = true;
// Display error message in the popup
@@ -826,46 +833,21 @@ function onDisconnected() {
keepass.getNonce = function() {
return nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize));
-
- // New implementation
- const oldNonce = nacl.util.decodeBase64(keepass.nonce);
-
- let newNonce = [];
- for (let i = 0; i < 24; i++){
- newNonce[i] = oldNonce[i];
- }
-
- for (let i = 0; i < 24; i++) {
- newNonce[i]++;
- if (newNonce[i]) {
- break;
- }
- }
-
- console.log("New: " + newNonce);
- console.log("Old: " + oldNonce);
- keepass.nonce = nacl.util.encodeBase64(newNonce);
- return nacl.util.encodeBase64(oldNonce);
};
keepass.incrementedNonce = function(nonce) {
const oldNonce = nacl.util.decodeBase64(nonce);
+ let newNonce = oldNonce.slice(0);
- // TODO: fix the incrementation, it's not complete yet
- let newNonce = [];
- for (let i = 0; i < 24; i++){
- newNonce[i] = oldNonce[i];
+ // from libsodium/utils.c
+ let i = 0;
+ let c = 1;
+ for (; i < newNonce.length; ++i) {
+ c += newNonce[i];
+ newNonce[i] = c;
+ c >>= 8;
}
- for (let i = 0; i < 24; i++) {
- newNonce[i]++;
- if (newNonce[i]) {
- break;
- }
- }
-
- console.log("New: " + newNonce);
- console.log("Old: " + oldNonce);
return nacl.util.encodeBase64(newNonce);
};
diff --git a/keepassxc-protocol.md b/keepassxc-protocol.md
index 703d627..8ac2107 100644
--- a/keepassxc-protocol.md
+++ b/keepassxc-protocol.md
@@ -7,7 +7,7 @@ Now the requests are encrypted by [TweetNaCl.js](https://github.com/dchest/tweet
2. When KeePassXC receives the public key it generates its own key pair and transfers the public key to keepassxc-browser
3. All messages between the browser extension and KeePassXC are now encrypted.
4. When keepassxc-browser sends a message it is encrypted with KeePassXC's public key, a random generated nonce and keepassxc-browser's secret key.
-5. When KeePassXC sends a message it is encrypted with keepassxc-browser's public key etc.
+5. When KeePassXC sends a message it is encrypted with keepassxc-browser's public key and an incremented nonce.
6. Databases are stored based on the current public key used with `associate`. A new key pair for data transfer is generated each time keepassxc-browser is launched.
Encrypted messages are built with these JSON parameters:
From ffd6f088223119d5c3afde2ed84e1b8d839d1fd6 Mon Sep 17 00:00:00 2001
From: varjolintu
Date: Mon, 27 Nov 2017 18:15:58 +0200
Subject: [PATCH 23/24] Changelog
---
CHANGELOG | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG b/CHANGELOG
index afe4ec6..76ce284 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,4 @@
-0.4.2 (??-??-2017)
+0.4.2 (27-11-2017)
=========================
- Fixed HTTP authentication with multiple credentials (credits to smorks)
- Fixed error handling when decrypt fails
From 9341ac83808aa8362e954b8b6df8b6b571266ff2 Mon Sep 17 00:00:00 2001
From: varjolintu
Date: Fri, 1 Dec 2017 11:10:53 +0200
Subject: [PATCH 24/24] Added issue template
---
.github/ISSUE_TEMPLATE.md | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 .github/ISSUE_TEMPLATE.md
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..941323e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,32 @@
+
+
+## Expected Behavior
+
+
+
+## Current Behavior
+
+
+
+## Possible Solution
+
+
+
+## Steps to Reproduce (for bugs)
+
+
+1.
+2.
+3.
+4.
+
+## Debug info
+
+
+## General Info
+KeePassXC fork - VERSION
+keepassxc-browser - VERSION
+
+Operating system: OS
+Browser: BROWSER
+Proxy used: YES/NO