From c392fe28606eefa0c814e5c25d641f5ffe623186 Mon Sep 17 00:00:00 2001 From: Michael Krelin Date: Mon, 30 Jun 2014 18:20:13 +0000 Subject: Merge remote-tracking branch 'github/master' into nmaster --- diff --git a/README.md b/README.md index e44df48..8e7cb6b 100644 --- a/README.md +++ b/README.md @@ -11,20 +11,25 @@ Since passwords are the most common type of private information that you need to Read more on the [Clipperz website][home]. -[home]: http://www.clipperz.com +[home]: https://clipperz.is -## Why an open source version +## Why an open source version of Clipperz? -Because we want to enable as many people as possible to play with our code. So that you can start trusting it, the code not the developers. +Because we want to enable as many people as possible to play with our code. So that they can start trusting it. The code, not its developers. -In order to allow you to inspect the code and analyze the traffic it generates between client and server, we had to provide an easy way to locally deploy the whole service. +In order to allow anyone not just to inspect the source code, but also to analyze the traffic it generates between client and server, we made available this open source version as an easy way to locally deploy the whole password manager web app on your machine. You can choose among the available backends (PHP/MySQL, Python/AppEngine, …) or [contribute][CA] your own. -Feel free to host on your machine a web service identical to [Clipperz online password manager][home]. You can choose among **multiple backends** (PHP/MySQL, Python/AppEngine, …) or you can [contribute][CA] your own. +Whatever is your motivation for playing with Clipperz code, we would love to hear from you: [get in contact][contact]! -Whatever is your motivation, we would love to hear from you: [get in contact!][contact] +## Security warning -[CA]: http://www.clipperz.com/open_source/contributor_agreement -[contact]: http://www.clipperz.com/about/contacts +The open source version of Clipperz is suitable for **testing and educational purposes only**. Do not use it as an actual password management solution. + +As an example, the current PHP backend lacks several critical capabilities such as bot protection and concurrent sessions management, moreover it could be vulnerable to serious threats (SQL injections, remote code execution, ...). + +[CA]: https://clipperz.is/open_source/contributor_agreement +[contact]: https://clipperz.is/about/contacts +[clipperz]: https://clipperz.is ## Donations @@ -34,19 +39,16 @@ Our favorite payment method is clearly Bitcoin ([learn why here][why]), but you **To make your donation visit [this page][donations]. Thanks!** -[why]: http://www.clipperz.com/pricing/why_bitcoin -[donations]: http://www.clipperz.com/donations +[why]: https://clipperz.is/pricing/why_bitcoin +[donations]: https://clipperz.is/donations ## License -ALL the code included in this project, if not otherwise stated, is released with the [AGPL v3][agpl] license (see `LICENSE.txt`), and all rights are reserved to Clipperz Srl. For any use not allowed by the AGPL license, please [contact us][contact] to inquire about licensing options for commercial applications. +ALL the code included in this project, if not otherwise stated, is released with the [AGPL v3][agpl] license (see `LICENSE.txt`), and all rights are reserved to Clipperz Srl. For any use not allowed by the AGPL license, please [contact us][contact] to inquire about licensing options for commercial applications. [agpl]: http://www.gnu.org/licenses/agpl.html -## Warnings -Please note that the open source version of Clipperz Password Manager may not be suitable for mass deployments, depending on how robust is the backend you select. As an example, the current PHP backend lacks several critical capabilities such as bot protection and concurrent sessions management. - ## Contributions Your contributions to Clipperz are very welcome! In order to avoid jeopardizing the ownership of the code base, we will require every developer to sign the Clipperz [Contributor Agreement][CA] @@ -115,4 +117,4 @@ Once the index.html files have been built (one for each frontend) and a backend This application has not been fully tested, so there may be still problems due to the new build script or to the new repository structure. So, for the moment, **use it at your own risk!** -[pog]: http://www.phpobjectgenerator.com/ \ No newline at end of file +[pog]: http://www.phpobjectgenerator.com/ diff --git a/frontend/beta/js/Clipperz/Base.js b/frontend/beta/js/Clipperz/Base.js index cf40314..1c6faa1 100644 --- a/frontend/beta/js/Clipperz/Base.js +++ b/frontend/beta/js/Clipperz/Base.js @@ -246,6 +246,34 @@ MochiKit.Base.update(Clipperz.Base, { return result; }, + 'javascriptInjectionPattern': new RegExp("javascript:\/\/\"", "g"), + + 'sanitizeUrl': function(aValue) { + var result; + + if ((aValue != null) && this.javascriptInjectionPattern.test(aValue)) { + result = aValue.replace(this.javascriptInjectionPattern, ''); + console.log("sanitized url", aValue, result); + } else { + result = aValue; + } + + return result; + }, + + 'sanitizeFavicon': function(aValue) { + var result; + + if ((aValue != null) && this.javascriptInjectionPattern.test(aValue)) { + result = aValue.replace(this.javascriptInjectionPattern, ''); + console.log("sanitized favicon", aValue, result); + } else { + result = aValue; + } + + return result; + }, + //------------------------------------------------------------------------- 'exception': { diff --git a/frontend/beta/js/Clipperz/Crypto/PRNG.js b/frontend/beta/js/Clipperz/Crypto/PRNG.js index b5c3f8a..6fdeca4 100644 --- a/frontend/beta/js/Clipperz/Crypto/PRNG.js +++ b/frontend/beta/js/Clipperz/Crypto/PRNG.js @@ -197,12 +197,6 @@ Clipperz.Crypto.PRNG.TimeRandomnessSource.prototype = MochiKit.Base.update(new C }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); @@ -245,12 +239,12 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new var numberOfRandomBitsCollected; numberOfRandomBitsCollected = this.numberOfRandomBitsCollected(); - collectetBits = this.randomBitsCollector() | (aValue << numberOfRandomBitsCollected); - this.setRandomBitsCollector(collectetBits); + collectedBits = this.randomBitsCollector() | (aValue << numberOfRandomBitsCollected); + this.setRandomBitsCollector(collectedBits); numberOfRandomBitsCollected += this.numberOfBitsToCollectAtEachEvent(); if (numberOfRandomBitsCollected == 8) { - this.updateGeneratorWithValue(collectetBits); + this.updateGeneratorWithValue(collectedBits); numberOfRandomBitsCollected = 0; this.setRandomBitsCollector(0); } @@ -289,96 +283,54 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //***************************************************************************** -Clipperz.Crypto.PRNG.KeyboardRandomnessSource = function(args) { +Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource = function(args) { args = args || {}; - Clipperz.Crypto.PRNG.RandomnessSource.call(this, args); - this._randomBitsCollector = 0; - this._numberOfRandomBitsCollected = 0; + this._intervalTime = args.intervalTime || 1000; + this._browserCrypto = args.browserCrypto; - MochiKit.Signal.connect(document, 'onkeypress', this, 'collectEntropy'); + Clipperz.Crypto.PRNG.RandomnessSource.call(this, args); + this.collectEntropy(); return this; } -Clipperz.Crypto.PRNG.KeyboardRandomnessSource.prototype = MochiKit.Base.update(new Clipperz.Crypto.PRNG.RandomnessSource, { - - //------------------------------------------------------------------------- - - 'randomBitsCollector': function() { - return this._randomBitsCollector; - }, +Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource.prototype = MochiKit.Base.update(new Clipperz.Crypto.PRNG.RandomnessSource, { - 'setRandomBitsCollector': function(aValue) { - this._randomBitsCollector = aValue; + 'intervalTime': function() { + return this._intervalTime; }, - - 'appendRandomBitToRandomBitsCollector': function(aValue) { - var collectedBits; - var numberOfRandomBitsCollected; - - numberOfRandomBitsCollected = this.numberOfRandomBitsCollected(); - collectetBits = this.randomBitsCollector() | (aValue << numberOfRandomBitsCollected); - this.setRandomBitsCollector(collectetBits); - numberOfRandomBitsCollected ++; - - if (numberOfRandomBitsCollected == 8) { - this.updateGeneratorWithValue(collectetBits); - numberOfRandomBitsCollected = 0; - this.setRandomBitsCollector(0); - } - - this.setNumberOfRandomBitsCollected(numberOfRandomBitsCollected) + + 'browserCrypto': function () { + return this._browserCrypto; }, //------------------------------------------------------------------------- - 'numberOfRandomBitsCollected': function() { - return this._numberOfRandomBitsCollected; - }, - - 'setNumberOfRandomBitsCollected': function(aValue) { - this._numberOfRandomBitsCollected = aValue; - }, + 'collectEntropy': function() { + var bytesToCollect; - //------------------------------------------------------------------------- + if (this.boostMode() == true) { + bytesToCollect = 64; + } else { + bytesToCollect = 8; + } - 'collectEntropy': function(anEvent) { -/* - var mouseLocation; - var randomBit; - - mouseLocation = anEvent.mouse().client; - - randomBit = ((mouseLocation.x ^ mouseLocation.y) & 0x1); - this.appendRandomBitToRandomBitsCollector(randomBit); -*/ - }, - - //------------------------------------------------------------------------- + var randomValuesArray = new Uint8Array(bytesToCollect); + this.browserCrypto().getRandomValues(randomValuesArray); + for (var i = 0; i < randomValuesArray.length; i++) { + this.updateGeneratorWithValue(randomValuesArray[i]); + } - 'numberOfRandomBits': function() { - return 1; + setTimeout(this.collectEntropy, this.intervalTime()); }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); @@ -607,21 +559,16 @@ MochiKit.Logging.logWarning("Fortuna generator has not enough entropy, yet!"); 'deferredEntropyCollection': function(aValue) { var result; -//MochiKit.Logging.logDebug(">>> PRNG.deferredEntropyCollection"); if (this.isReadyToGenerateRandomValues()) { -//MochiKit.Logging.logDebug("--- PRNG.deferredEntropyCollection - 1"); result = aValue; } else { -//MochiKit.Logging.logDebug("--- PRNG.deferredEntropyCollection - 2"); var deferredResult; Clipperz.NotificationCenter.notify(this, 'updatedProgressState', 'collectingEntropy', true); deferredResult = new MochiKit.Async.Deferred(); -// deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("1.2.1 - PRNG.deferredEntropyCollection - 1: " + res); return res;}); deferredResult.addCallback(MochiKit.Base.partial(MochiKit.Async.succeed, aValue)); -// deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("1.2.2 - PRNG.deferredEntropyCollection - 2: " + res); return res;}); MochiKit.Signal.connect(this, 'readyToGenerateRandomBytes', deferredResult, @@ -629,7 +576,6 @@ MochiKit.Logging.logWarning("Fortuna generator has not enough entropy, yet!"); result = deferredResult; } -//MochiKit.Logging.logDebug("<<< PRNG.deferredEntropyCollection - result: " + result); return result; }, @@ -643,7 +589,7 @@ MochiKit.Logging.logWarning("Fortuna generator has not enough entropy, yet!"); }, //------------------------------------------------------------------------- - +/* 'dump': function(appendToDoc) { var tbl; var i,c; @@ -749,7 +695,7 @@ MochiKit.Logging.logWarning("Fortuna generator has not enough entropy, yet!"); return tbl; }, - +*/ //----------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); @@ -824,16 +770,26 @@ Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { //............................................................. // - // KeyboardRandomnessSource + // CryptoRandomRandomnessSource // //............................................................. { var newRandomnessSource; + var browserCrypto; - newRandomnessSource = new Clipperz.Crypto.PRNG.KeyboardRandomnessSource(); - _clipperz_crypt_prng_defaultPRNG.addRandomnessSource(newRandomnessSource); + if (window.crypto && window.crypto.getRandomValues) { + browserCrypto = window.crypto; + } else if (window.msCrypto && window.msCrypto.getRandomValues) { + browserCrypto = window.msCrypto; + } else { + browserCrypto = null; + } + + if (browserCrypto != null) { + newRandomnessSource = new Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource({'browserCrypto':browserCrypto}); + _clipperz_crypt_prng_defaultPRNG.addRandomnessSource(newRandomnessSource); + } } - } return _clipperz_crypt_prng_defaultPRNG; diff --git a/frontend/beta/js/Clipperz/Crypto/SRP.js b/frontend/beta/js/Clipperz/Crypto/SRP.js index 8cc80ba..8c522ad 100644 --- a/frontend/beta/js/Clipperz/Crypto/SRP.js +++ b/frontend/beta/js/Clipperz/Crypto/SRP.js @@ -44,6 +44,8 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, { '_n': null, '_g': null, + '_k': null, + //------------------------------------------------------------------------- 'n': function() { @@ -64,6 +66,15 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, { return Clipperz.Crypto.SRP._g; }, + 'k': function() { + if (Clipperz.Crypto.SRP._k == null) { +// Clipperz.Crypto.SRP._k = new Clipperz.Crypto.BigInt(this.stringHash(this.n().asString() + this.g().asString()), 16); + Clipperz.Crypto.SRP._k = new Clipperz.Crypto.BigInt("64398bff522814e306a97cb9bfc4364b7eed16a8c17c5208a40a2bad2933c8e", 16); + } + + return Clipperz.Crypto.SRP._k; + }, + //----------------------------------------------------------------------------- 'exception': { @@ -129,7 +140,6 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { if (this._a == null) { this._a = new Clipperz.Crypto.BigInt(Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32).toHexString().substring(2), 16); // this._a = new Clipperz.Crypto.BigInt("37532428169486597638072888476611365392249575518156687476805936694442691012367", 10); -//MochiKit.Logging.logDebug("SRP a: " + this._a); } return this._a; @@ -139,14 +149,12 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'A': function () { if (this._A == null) { - // Warning: this value should be strictly greater than zero: how should we perform this check? + // Warning: this value should be strictly greater than zero this._A = Clipperz.Crypto.SRP.g().powerModule(this.a(), Clipperz.Crypto.SRP.n()); - - if (this._A.equals(0)) { -MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'A' to 0."); + if (this._A.equals(0) || negative(this._A)) { + MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'A' to 0."); throw Clipperz.Crypto.SRP.exception.InvalidValue; } -//MochiKit.Logging.logDebug("SRP A: " + this._A); } return this._A; @@ -156,7 +164,6 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'A' to 's': function () { return this._s; -//MochiKit.Logging.logDebug("SRP s: " + this._S); }, 'set_s': function(aValue) { @@ -170,12 +177,10 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'A' to }, 'set_B': function(aValue) { - // Warning: this value should be strictly greater than zero: how should we perform this check? - if (! aValue.equals(0)) { - this._B = aValue; -//MochiKit.Logging.logDebug("SRP B: " + this._B); - } else { -MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to 0."); + // Warning: this value should be strictly greater than zero + this._B = aValue; + if (this._B.equals(0) || negative(this._B)) { + MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to 0."); throw Clipperz.Crypto.SRP.exception.InvalidValue; } }, @@ -185,7 +190,6 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to 'x': function () { if (this._x == null) { this._x = new Clipperz.Crypto.BigInt(this.stringHash(this.s().asString(16, 64) + this.P()), 16); -//MochiKit.Logging.logDebug("SRP x: " + this._x); } return this._x; @@ -195,8 +199,7 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to 'u': function () { if (this._u == null) { - this._u = new Clipperz.Crypto.BigInt(this.stringHash(this.B().asString()), 16); -//MochiKit.Logging.logDebug("SRP u: " + this._u); + this._u = new Clipperz.Crypto.BigInt(this.stringHash(this.A().asString() + this.B().asString()), 16); } return this._u; @@ -213,11 +216,16 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to srp = Clipperz.Crypto.SRP; this._S = bigint.powerModule( - bigint.subtract(this.B(), bigint.powerModule(srp.g(), this.x(), srp.n())), - bigint.add(this.a(), bigint.multiply(this.u(), this.x())), - srp.n() + bigint.subtract( + this.B(), + bigint.multiply( + Clipperz.Crypto.SRP.k(), + bigint.powerModule(srp.g(), this.x(), srp.n()) + ) + ), + bigint.add(this.a(), bigint.multiply(this.u(), this.x())), + srp.n() ) -//MochiKit.Logging.logDebug("SRP S: " + this._S); } return this._S; @@ -228,7 +236,6 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to 'K': function () { if (this._K == null) { this._K = this.stringHash(this.S().asString()); -//MochiKit.Logging.logDebug("SRP K: " + this._K); } return this._K; @@ -238,8 +245,20 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to 'M1': function () { if (this._M1 == null) { - this._M1 = this.stringHash(this.A().asString(10) + this.B().asString(10) + this.K()); -//MochiKit.Logging.logDebug("SRP M1: " + this._M1); +// this._M1 = this.stringHash(this.A().asString(10) + this.B().asString(10) + this.K()); + + // http://srp.stanford.edu/design.html + // User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K) + + this._M1 = this.stringHash( + "597626870978286801440197562148588907434001483655788865609375806439877501869636875571920406529" + + this.stringHash(this.C()) + + this.s().asString() + + this.A().asString() + + this.B().asString() + + this.K() + ); +//console.log("M1", this._M1); } return this._M1; @@ -250,7 +269,7 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to 'M2': function () { if (this._M2 == null) { this._M2 = this.stringHash(this.A().asString(10) + this.M1() + this.K()); -//MochiKit.Logging.logDebug("SRP M2: " + this._M2); +//console.log("M2", this._M2); } return this._M2; diff --git a/frontend/beta/js/Clipperz/PM/BookmarkletProcessor.js b/frontend/beta/js/Clipperz/PM/BookmarkletProcessor.js index 2295d3f..369b9ce 100644 --- a/frontend/beta/js/Clipperz/PM/BookmarkletProcessor.js +++ b/frontend/beta/js/Clipperz/PM/BookmarkletProcessor.js @@ -138,7 +138,7 @@ Clipperz.PM.BookmarkletProcessor.prototype = MochiKit.Base.update(null, { if (this._hostname == null) { var actionUrl; - actionUrl = this.configuration()['form']['attributes']['action']; + actionUrl = Clipperz.Base.sanitizeUrl(this.configuration()['form']['attributes']['action']); //MochiKit.Logging.logDebug("+++ actionUrl: " + actionUrl); this._hostname = actionUrl.replace(/^https?:\/\/([^\/]*)\/.*/, '$1'); } diff --git a/frontend/beta/js/Clipperz/PM/Components/RecordDetail/DirectLoginBindingComponent.js b/frontend/beta/js/Clipperz/PM/Components/RecordDetail/DirectLoginBindingComponent.js index 0e4640e..a5a4697 100644 --- a/frontend/beta/js/Clipperz/PM/Components/RecordDetail/DirectLoginBindingComponent.js +++ b/frontend/beta/js/Clipperz/PM/Components/RecordDetail/DirectLoginBindingComponent.js @@ -100,7 +100,7 @@ YAHOO.extendX(Clipperz.PM.Components.RecordDetail.DirectLoginBindingComponent, C result.push(option); for (recordFieldKey in recordFields) { // TODO: remove the value: field and replace it with element.dom.value = - option = {tag:'option', value:recordFieldKey, html:recordFields[recordFieldKey].label()} + option = {tag:'option', value:recordFieldKey, html:Clipperz.Base.sanitizeString(recordFields[recordFieldKey].label())} if (recordFieldKey == this.directLoginBinding().fieldKey()) { option['selected'] = true; } @@ -150,7 +150,7 @@ YAHOO.extendX(Clipperz.PM.Components.RecordDetail.DirectLoginBindingComponent, C this.getElement('editModeBox').hide(); this.getElement('viewModeBox').show(); - this.getElement('viewValue').update(this.directLoginBinding().field().label()); + this.getElement('viewValue').update(Clipperz.Base.sanitizeString(this.directLoginBinding().field().label())); //MochiKit.Logging.logDebug("<<< DirectLoginBindingComponent.updateViewMode"); }, diff --git a/frontend/beta/js/Clipperz/PM/DataModel/DirectLogin.js b/frontend/beta/js/Clipperz/PM/DataModel/DirectLogin.js index c0cfa3c..56d9d59 100644 --- a/frontend/beta/js/Clipperz/PM/DataModel/DirectLogin.js +++ b/frontend/beta/js/Clipperz/PM/DataModel/DirectLogin.js @@ -38,7 +38,7 @@ Clipperz.PM.DataModel.DirectLogin = function(args) { this._record = args.record || null; this._label = args.label || "unnamed record" this._reference = args.reference || Clipperz.PM.Crypto.randomKey(); - this._favicon = args.favicon || null; + this._favicon = Clipperz.Base.sanitizeFavicon(args.favicon) || null; this._bookmarkletVersion = args.bookmarkletVersion || "0.1"; this._directLoginInputs = null; @@ -102,9 +102,9 @@ Clipperz.PM.DataModel.DirectLogin.prototype = MochiKit.Base.update(null, { var actionUrl; var hostname; - actionUrl = this.formData()['attributes']['action']; + actionUrl = this.action(); hostname = actionUrl.replace(/^https?:\/\/([^\/]*)\/.*/, '$1'); - this._favicon = "http://" + hostname + "/favicon.ico"; + this._favicon = Clipperz.Base.sanitizeFavicon("http://" + hostname + "/favicon.ico"); } return this._favicon; @@ -137,6 +137,14 @@ Clipperz.PM.DataModel.DirectLogin.prototype = MochiKit.Base.update(null, { this._fixedFavicon = aValue; }, + 'action': function () { + var result; + + result = Clipperz.Base.sanitizeUrl(this.formData()['attributes']['action']); + + return result; + }, + //------------------------------------------------------------------------- 'bookmarkletVersion': function() { @@ -442,7 +450,7 @@ Clipperz.PM.DataModel.DirectLogin.prototype = MochiKit.Base.update(null, { //MochiKit.Logging.logDebug("### runDirectLogin - 4"); //console.log(this.formData()['attributes']); formElement = MochiKit.DOM.FORM(MochiKit.Base.update({id:'directLoginForm'}, { 'method':this.formData()['attributes']['method'], - 'action':this.formData()['attributes']['action']})); + 'action': this.action()})); //MochiKit.Logging.logDebug("### runDirectLogin - 5"); formSubmitFunction = MochiKit.Base.method(formElement, 'submit'); //MochiKit.Logging.logDebug("### runDirectLogin - 6"); @@ -487,9 +495,9 @@ Clipperz.PM.DataModel.DirectLogin.prototype = MochiKit.Base.update(null, { //console.log("formData.attributes", this.formData()['attributes']); // if (/^javascript/.test(this.formData()['attributes']['action'])) { - if ((/^(https?|webdav|ftp)\:/.test(this.formData()['attributes']['action']) == false) && - (this.formData()['attributes']['type'] != 'http_auth')) - { + if ((/^(https?|webdav|ftp)\:/.test(this.action()) == false) && + (this.formData()['attributes']['type'] != 'http_auth') + ) { var messageBoxConfiguration; if (typeof(aNewWindow) != 'undefined') { diff --git a/frontend/beta/js/Clipperz/PM/DataModel/DirectLoginReference.js b/frontend/beta/js/Clipperz/PM/DataModel/DirectLoginReference.js index 236d7c9..ba302da 100644 --- a/frontend/beta/js/Clipperz/PM/DataModel/DirectLoginReference.js +++ b/frontend/beta/js/Clipperz/PM/DataModel/DirectLoginReference.js @@ -47,7 +47,7 @@ Clipperz.PM.DataModel.DirectLoginReference = function(args) { this._reference = args.reference; this._recordReference = args.record; this._label = args.label; - this._favicon = args.favicon || null; + this._favicon = Clipperz.Base.sanitizeFavicon(args.favicon) || null; this._directLogin = null; this._record = null; diff --git a/frontend/beta/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js b/frontend/beta/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js index 1a5caff..b0b9b63 100644 --- a/frontend/beta/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js +++ b/frontend/beta/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js @@ -37,6 +37,7 @@ Clipperz.PM.Proxy.Offline.DataStore = function(args) { this._tolls = {}; this._connections = {}; + this._C = null; this._b = null; this._B = null; this._A = null; @@ -144,6 +145,16 @@ Clipperz.PM.Proxy.Offline.DataStore.prototype = MochiKit.Base.update(null, { //========================================================================= + 'C': function() { + return this._C; + }, + + 'set_C': function(aValue) { + this._C = aValue; + }, + + //------------------------------------------------------------------------- + 'b': function() { return this._b; }, @@ -236,8 +247,8 @@ Clipperz.PM.Proxy.Offline.DataStore.prototype = MochiKit.Base.update(null, { }, //========================================================================= - - 'processMessage': function(aFunctionName, someParameters) { + + 'processMessage': function (aFunctionName, someParameters) { var result; switch(aFunctionName) { @@ -303,14 +314,14 @@ Clipperz.PM.Proxy.Offline.DataStore.prototype = MochiKit.Base.update(null, { throw "user already exists"; } } else { - throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly; + throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly; } result = { result: { 'lock': this.data()['users'][someParameters['credentials']['C']]['lock'], 'result': 'done' - }, + }, toll: this.getTollForRequestType('CONNECT') } @@ -340,9 +351,10 @@ Clipperz.PM.Proxy.Offline.DataStore.prototype = MochiKit.Base.update(null, { } randomBytes = Clipperz.Crypto.Base.generateRandomSeed(); + this.set_C(someParameters.parameters.C); this.set_b(new Clipperz.Crypto.BigInt(randomBytes, 16)); v = new Clipperz.Crypto.BigInt(this.userData()['v'], 16); - this.set_B(v.add(Clipperz.Crypto.SRP.g().powerModule(this.b(), Clipperz.Crypto.SRP.n()))); + this.set_B((Clipperz.Crypto.SRP.k().multiply(v)).add(Clipperz.Crypto.SRP.g().powerModule(this.b(), Clipperz.Crypto.SRP.n()))); this.set_A(someParameters.parameters.A); @@ -351,21 +363,36 @@ Clipperz.PM.Proxy.Offline.DataStore.prototype = MochiKit.Base.update(null, { nextTollRequestType = 'CONNECT'; } else if (someParameters.message == "credentialCheck") { - var v, u, S, A, K, M1; + var v, u, s, S, A, K, M1; + var stringHash = function (aValue) { + return Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aValue)).toHexString().substring(2); + }; //console.log(">>> Proxy.Offline.DataStore._handshake.credentialCheck", someParameters); v = new Clipperz.Crypto.BigInt(this.userData()['v'], 16); - u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(this.B().asString(10))).toHexString(), 16); A = new Clipperz.Crypto.BigInt(this.A(), 16); + u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + this.B().asString(10))).toHexString(), 16); + s = new Clipperz.Crypto.BigInt(this.userData()['s'], 16); S = (A.multiply(v.powerModule(u, Clipperz.Crypto.SRP.n()))).powerModule(this.b(), Clipperz.Crypto.SRP.n()); - K = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(S.asString(10))).toHexString().slice(2); + K = stringHash(S.asString(10)); - M1 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + this.B().asString(10) + K)).toHexString().slice(2); + M1 = stringHash( + "597626870978286801440197562148588907434001483655788865609375806439877501869636875571920406529" + + stringHash(this.C()) + + s.asString(10) + + A.asString(10) + + this.B().asString(10) + + K + ); if (someParameters.parameters.M1 == M1) { var M2; - M2 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + someParameters.parameters.M1 + K)).toHexString().slice(2); + M2 = stringHash( + A.asString(10) + + someParameters.parameters.M1 + + K + ); result['M2'] = M2; } else { throw new Error("Client checksum verification failed! Expected <" + M1 + ">, received <" + someParameters.parameters.M1 + ">.", "Error"); diff --git a/frontend/delta/js/Clipperz/Crypto/PRNG.js b/frontend/delta/js/Clipperz/Crypto/PRNG.js index c539f06..80d972f 100644 --- a/frontend/delta/js/Clipperz/Crypto/PRNG.js +++ b/frontend/delta/js/Clipperz/Crypto/PRNG.js @@ -21,6 +21,8 @@ refer to http://www.clipperz.com. */ +"use strict"; + try { if (typeof(Clipperz.ByteArray) == 'undefined') { throw ""; }} catch (e) { throw "Clipperz.Crypto.PRNG depends on Clipperz.ByteArray!"; } @@ -197,12 +199,6 @@ Clipperz.Crypto.PRNG.TimeRandomnessSource.prototype = MochiKit.Base.update(new C }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); @@ -245,12 +241,12 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new var numberOfRandomBitsCollected; numberOfRandomBitsCollected = this.numberOfRandomBitsCollected(); - collectetBits = this.randomBitsCollector() | (aValue << numberOfRandomBitsCollected); - this.setRandomBitsCollector(collectetBits); + collectedBits = this.randomBitsCollector() | (aValue << numberOfRandomBitsCollected); + this.setRandomBitsCollector(collectedBits); numberOfRandomBitsCollected += this.numberOfBitsToCollectAtEachEvent(); if (numberOfRandomBitsCollected == 8) { - this.updateGeneratorWithValue(collectetBits); + this.updateGeneratorWithValue(collectedBits); numberOfRandomBitsCollected = 0; this.setRandomBitsCollector(0); } @@ -289,96 +285,54 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //***************************************************************************** -Clipperz.Crypto.PRNG.KeyboardRandomnessSource = function(args) { +Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource = function(args) { args = args || {}; - Clipperz.Crypto.PRNG.RandomnessSource.call(this, args); - this._randomBitsCollector = 0; - this._numberOfRandomBitsCollected = 0; + this._intervalTime = args.intervalTime || 1000; + this._browserCrypto = args.browserCrypto; - MochiKit.Signal.connect(document, 'onkeypress', this, 'collectEntropy'); + Clipperz.Crypto.PRNG.RandomnessSource.call(this, args); + this.collectEntropy(); return this; } -Clipperz.Crypto.PRNG.KeyboardRandomnessSource.prototype = MochiKit.Base.update(new Clipperz.Crypto.PRNG.RandomnessSource, { +Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource.prototype = MochiKit.Base.update(new Clipperz.Crypto.PRNG.RandomnessSource, { - //------------------------------------------------------------------------- - - 'randomBitsCollector': function() { - return this._randomBitsCollector; - }, - - 'setRandomBitsCollector': function(aValue) { - this._randomBitsCollector = aValue; + 'intervalTime': function() { + return this._intervalTime; }, - - 'appendRandomBitToRandomBitsCollector': function(aValue) { - var collectedBits; - var numberOfRandomBitsCollected; - - numberOfRandomBitsCollected = this.numberOfRandomBitsCollected(); - collectetBits = this.randomBitsCollector() | (aValue << numberOfRandomBitsCollected); - this.setRandomBitsCollector(collectetBits); - numberOfRandomBitsCollected ++; - - if (numberOfRandomBitsCollected == 8) { - this.updateGeneratorWithValue(collectetBits); - numberOfRandomBitsCollected = 0; - this.setRandomBitsCollector(0); - } - - this.setNumberOfRandomBitsCollected(numberOfRandomBitsCollected) + + 'browserCrypto': function () { + return this._browserCrypto; }, //------------------------------------------------------------------------- - 'numberOfRandomBitsCollected': function() { - return this._numberOfRandomBitsCollected; - }, - - 'setNumberOfRandomBitsCollected': function(aValue) { - this._numberOfRandomBitsCollected = aValue; - }, + 'collectEntropy': function() { + var bytesToCollect; - //------------------------------------------------------------------------- + if (this.boostMode() == true) { + bytesToCollect = 64; + } else { + bytesToCollect = 8; + } - 'collectEntropy': function(anEvent) { -/* - var mouseLocation; - var randomBit; - - mouseLocation = anEvent.mouse().client; - - randomBit = ((mouseLocation.x ^ mouseLocation.y) & 0x1); - this.appendRandomBitToRandomBitsCollector(randomBit); -*/ - }, - - //------------------------------------------------------------------------- + var randomValuesArray = new Uint8Array(bytesToCollect); + this.browserCrypto().getRandomValues(randomValuesArray); + for (var i = 0; i < randomValuesArray.length; i++) { + this.updateGeneratorWithValue(randomValuesArray[i]); + } - 'numberOfRandomBits': function() { - return 1; + setTimeout(this.collectEntropy, this.intervalTime()); }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); @@ -635,7 +589,7 @@ Clipperz.logWarning("Fortuna generator has not enough entropy, yet!"); }, //------------------------------------------------------------------------- - +/* 'dump': function(appendToDoc) { var tbl; var i,c; @@ -741,7 +695,7 @@ Clipperz.logWarning("Fortuna generator has not enough entropy, yet!"); return tbl; }, - +*/ //----------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); @@ -784,7 +738,7 @@ Clipperz.Crypto.PRNG.Random.prototype = MochiKit.Base.update(null, { //############################################################################# -_clipperz_crypt_prng_defaultPRNG = null; +var _clipperz_crypt_prng_defaultPRNG = null; Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { if (_clipperz_crypt_prng_defaultPRNG == null) { @@ -816,16 +770,26 @@ Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { //............................................................. // - // KeyboardRandomnessSource + // CryptoRandomRandomnessSource // //............................................................. { var newRandomnessSource; + var browserCrypto; - newRandomnessSource = new Clipperz.Crypto.PRNG.KeyboardRandomnessSource(); - _clipperz_crypt_prng_defaultPRNG.addRandomnessSource(newRandomnessSource); + if (window.crypto && window.crypto.getRandomValues) { + browserCrypto = window.crypto; + } else if (window.msCrypto && window.msCrypto.getRandomValues) { + browserCrypto = window.msCrypto; + } else { + browserCrypto = null; + } + + if (browserCrypto != null) { + newRandomnessSource = new Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource({'browserCrypto':browserCrypto}); + _clipperz_crypt_prng_defaultPRNG.addRandomnessSource(newRandomnessSource); + } } - } return _clipperz_crypt_prng_defaultPRNG; diff --git a/frontend/delta/js/Clipperz/Crypto/SRP.js b/frontend/delta/js/Clipperz/Crypto/SRP.js index 597e72d..6898dfb 100644 --- a/frontend/delta/js/Clipperz/Crypto/SRP.js +++ b/frontend/delta/js/Clipperz/Crypto/SRP.js @@ -44,6 +44,8 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, { '_n': null, '_g': null, + '_k': null, + //------------------------------------------------------------------------- 'n': function() { @@ -64,6 +66,15 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, { return Clipperz.Crypto.SRP._g; }, + 'k': function() { + if (Clipperz.Crypto.SRP._k == null) { +// Clipperz.Crypto.SRP._k = new Clipperz.Crypto.BigInt(this.stringHash(this.n().asString() + this.g().asString()), 16); + Clipperz.Crypto.SRP._k = new Clipperz.Crypto.BigInt("64398bff522814e306a97cb9bfc4364b7eed16a8c17c5208a40a2bad2933c8e", 16); + } + + return Clipperz.Crypto.SRP._k; + }, + //----------------------------------------------------------------------------- 'exception': { @@ -138,10 +149,9 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'A': function () { if (this._A == null) { - // Warning: this value should be strictly greater than zero: how should we perform this check? + // Warning: this value should be strictly greater than zero this._A = Clipperz.Crypto.SRP.g().powerModule(this.a(), Clipperz.Crypto.SRP.n()); - - if (this._A.equals(0)) { + if (this._A.equals(0) || negative(this._A)) { Clipperz.logError("Clipperz.Crypto.SRP.Connection: trying to set 'A' to 0."); throw Clipperz.Crypto.SRP.exception.InvalidValue; } @@ -167,10 +177,9 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { }, 'set_B': function(aValue) { - // Warning: this value should be strictly greater than zero: how should we perform this check? - if (! aValue.equals(0)) { - this._B = aValue; - } else { + // Warning: this value should be strictly greater than zero + this._B = aValue; + if (this._B.equals(0) || negative(this._B)) { Clipperz.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to 0."); throw Clipperz.Crypto.SRP.exception.InvalidValue; } @@ -190,7 +199,7 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'u': function () { if (this._u == null) { - this._u = new Clipperz.Crypto.BigInt(this.stringHash(this.B().asString()), 16); + this._u = new Clipperz.Crypto.BigInt(this.stringHash(this.A().asString() + this.B().asString()), 16); } return this._u; @@ -207,9 +216,15 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { srp = Clipperz.Crypto.SRP; this._S = bigint.powerModule( - bigint.subtract(this.B(), bigint.powerModule(srp.g(), this.x(), srp.n())), - bigint.add(this.a(), bigint.multiply(this.u(), this.x())), - srp.n() + bigint.subtract( + this.B(), + bigint.multiply( + Clipperz.Crypto.SRP.k(), + bigint.powerModule(srp.g(), this.x(), srp.n()) + ) + ), + bigint.add(this.a(), bigint.multiply(this.u(), this.x())), + srp.n() ) } @@ -230,7 +245,20 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'M1': function () { if (this._M1 == null) { - this._M1 = this.stringHash(this.A().asString(10) + this.B().asString(10) + this.K()); +// this._M1 = this.stringHash(this.A().asString(10) + this.B().asString(10) + this.K()); + + // http://srp.stanford.edu/design.html + // User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K) + + this._M1 = this.stringHash( + "597626870978286801440197562148588907434001483655788865609375806439877501869636875571920406529" + + this.stringHash(this.C()) + + this.s().asString() + + this.A().asString() + + this.B().asString() + + this.K() + ); +//console.log("M1", this._M1); } return this._M1; @@ -241,6 +269,7 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'M2': function () { if (this._M2 == null) { this._M2 = this.stringHash(this.A().asString(10) + this.M1() + this.K()); +//console.log("M2", this._M2); } return this._M2; diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js index 3f16f70..d03f873 100644 --- a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js +++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js @@ -88,7 +88,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.LocalStorageDataStore, Clipperz.P randomBytes = Clipperz.Crypto.Base.generateRandomSeed(); aConnection['b'] = new Clipperz.Crypto.BigInt(randomBytes, 16); v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16); - aConnection['B'] = v.add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n())); + aConnection['B'] = (Clipperz.Crypto.SRP.k().multiply(v)).add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n())); aConnection['A'] = someParameters.parameters.A; @@ -97,20 +97,35 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.LocalStorageDataStore, Clipperz.P nextTollRequestType = 'CONNECT'; } else if (someParameters.message == "credentialCheck") { - var v, u, S, A, K, M1; + var v, u, s, S, A, K, M1; + var stringHash = function (aValue) { + return Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aValue)).toHexString().substring(2); + }; v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16); - u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aConnection['B'].asString(10))).toHexString(), 16); A = new Clipperz.Crypto.BigInt(aConnection['A'], 16); + u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + aConnection['B'].asString(10))).toHexString(), 16); + s = new Clipperz.Crypto.BigInt(aConnection['userData']['s'], 16); S = (A.multiply(v.powerModule(u, Clipperz.Crypto.SRP.n()))).powerModule(aConnection['b'], Clipperz.Crypto.SRP.n()); - K = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(S.asString(10))).toHexString().slice(2); + K = stringHash(S.asString(10)); - M1 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + aConnection['B'].asString(10) + K)).toHexString().slice(2); + M1 = stringHash( + "597626870978286801440197562148588907434001483655788865609375806439877501869636875571920406529" + + stringHash(aConnection['C']) + + s.asString(10) + + A.asString(10) + + aConnection['B'].asString(10) + + K + ); if (someParameters.parameters.M1 == M1) { var M2; - M2 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + someParameters.parameters.M1 + K)).toHexString().slice(2); + M2 = stringHash( + A.asString(10) + + someParameters.parameters.M1 + + K + ); result['M2'] = M2; } else { throw new Error("Client checksum verification failed! Expected <" + M1 + ">, received <" + someParameters.parameters.M1 + ">.", "Error"); diff --git a/frontend/gamma/js/Clipperz/Crypto/PRNG.js b/frontend/gamma/js/Clipperz/Crypto/PRNG.js index c539f06..80d972f 100644 --- a/frontend/gamma/js/Clipperz/Crypto/PRNG.js +++ b/frontend/gamma/js/Clipperz/Crypto/PRNG.js @@ -21,6 +21,8 @@ refer to http://www.clipperz.com. */ +"use strict"; + try { if (typeof(Clipperz.ByteArray) == 'undefined') { throw ""; }} catch (e) { throw "Clipperz.Crypto.PRNG depends on Clipperz.ByteArray!"; } @@ -197,12 +199,6 @@ Clipperz.Crypto.PRNG.TimeRandomnessSource.prototype = MochiKit.Base.update(new C }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); @@ -245,12 +241,12 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new var numberOfRandomBitsCollected; numberOfRandomBitsCollected = this.numberOfRandomBitsCollected(); - collectetBits = this.randomBitsCollector() | (aValue << numberOfRandomBitsCollected); - this.setRandomBitsCollector(collectetBits); + collectedBits = this.randomBitsCollector() | (aValue << numberOfRandomBitsCollected); + this.setRandomBitsCollector(collectedBits); numberOfRandomBitsCollected += this.numberOfBitsToCollectAtEachEvent(); if (numberOfRandomBitsCollected == 8) { - this.updateGeneratorWithValue(collectetBits); + this.updateGeneratorWithValue(collectedBits); numberOfRandomBitsCollected = 0; this.setRandomBitsCollector(0); } @@ -289,96 +285,54 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //***************************************************************************** -Clipperz.Crypto.PRNG.KeyboardRandomnessSource = function(args) { +Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource = function(args) { args = args || {}; - Clipperz.Crypto.PRNG.RandomnessSource.call(this, args); - this._randomBitsCollector = 0; - this._numberOfRandomBitsCollected = 0; + this._intervalTime = args.intervalTime || 1000; + this._browserCrypto = args.browserCrypto; - MochiKit.Signal.connect(document, 'onkeypress', this, 'collectEntropy'); + Clipperz.Crypto.PRNG.RandomnessSource.call(this, args); + this.collectEntropy(); return this; } -Clipperz.Crypto.PRNG.KeyboardRandomnessSource.prototype = MochiKit.Base.update(new Clipperz.Crypto.PRNG.RandomnessSource, { +Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource.prototype = MochiKit.Base.update(new Clipperz.Crypto.PRNG.RandomnessSource, { - //------------------------------------------------------------------------- - - 'randomBitsCollector': function() { - return this._randomBitsCollector; - }, - - 'setRandomBitsCollector': function(aValue) { - this._randomBitsCollector = aValue; + 'intervalTime': function() { + return this._intervalTime; }, - - 'appendRandomBitToRandomBitsCollector': function(aValue) { - var collectedBits; - var numberOfRandomBitsCollected; - - numberOfRandomBitsCollected = this.numberOfRandomBitsCollected(); - collectetBits = this.randomBitsCollector() | (aValue << numberOfRandomBitsCollected); - this.setRandomBitsCollector(collectetBits); - numberOfRandomBitsCollected ++; - - if (numberOfRandomBitsCollected == 8) { - this.updateGeneratorWithValue(collectetBits); - numberOfRandomBitsCollected = 0; - this.setRandomBitsCollector(0); - } - - this.setNumberOfRandomBitsCollected(numberOfRandomBitsCollected) + + 'browserCrypto': function () { + return this._browserCrypto; }, //------------------------------------------------------------------------- - 'numberOfRandomBitsCollected': function() { - return this._numberOfRandomBitsCollected; - }, - - 'setNumberOfRandomBitsCollected': function(aValue) { - this._numberOfRandomBitsCollected = aValue; - }, + 'collectEntropy': function() { + var bytesToCollect; - //------------------------------------------------------------------------- + if (this.boostMode() == true) { + bytesToCollect = 64; + } else { + bytesToCollect = 8; + } - 'collectEntropy': function(anEvent) { -/* - var mouseLocation; - var randomBit; - - mouseLocation = anEvent.mouse().client; - - randomBit = ((mouseLocation.x ^ mouseLocation.y) & 0x1); - this.appendRandomBitToRandomBitsCollector(randomBit); -*/ - }, - - //------------------------------------------------------------------------- + var randomValuesArray = new Uint8Array(bytesToCollect); + this.browserCrypto().getRandomValues(randomValuesArray); + for (var i = 0; i < randomValuesArray.length; i++) { + this.updateGeneratorWithValue(randomValuesArray[i]); + } - 'numberOfRandomBits': function() { - return 1; + setTimeout(this.collectEntropy, this.intervalTime()); }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); @@ -635,7 +589,7 @@ Clipperz.logWarning("Fortuna generator has not enough entropy, yet!"); }, //------------------------------------------------------------------------- - +/* 'dump': function(appendToDoc) { var tbl; var i,c; @@ -741,7 +695,7 @@ Clipperz.logWarning("Fortuna generator has not enough entropy, yet!"); return tbl; }, - +*/ //----------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); @@ -784,7 +738,7 @@ Clipperz.Crypto.PRNG.Random.prototype = MochiKit.Base.update(null, { //############################################################################# -_clipperz_crypt_prng_defaultPRNG = null; +var _clipperz_crypt_prng_defaultPRNG = null; Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { if (_clipperz_crypt_prng_defaultPRNG == null) { @@ -816,16 +770,26 @@ Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { //............................................................. // - // KeyboardRandomnessSource + // CryptoRandomRandomnessSource // //............................................................. { var newRandomnessSource; + var browserCrypto; - newRandomnessSource = new Clipperz.Crypto.PRNG.KeyboardRandomnessSource(); - _clipperz_crypt_prng_defaultPRNG.addRandomnessSource(newRandomnessSource); + if (window.crypto && window.crypto.getRandomValues) { + browserCrypto = window.crypto; + } else if (window.msCrypto && window.msCrypto.getRandomValues) { + browserCrypto = window.msCrypto; + } else { + browserCrypto = null; + } + + if (browserCrypto != null) { + newRandomnessSource = new Clipperz.Crypto.PRNG.CryptoRandomRandomnessSource({'browserCrypto':browserCrypto}); + _clipperz_crypt_prng_defaultPRNG.addRandomnessSource(newRandomnessSource); + } } - } return _clipperz_crypt_prng_defaultPRNG; diff --git a/frontend/gamma/js/Clipperz/Crypto/SRP.js b/frontend/gamma/js/Clipperz/Crypto/SRP.js index 597e72d..6898dfb 100644 --- a/frontend/gamma/js/Clipperz/Crypto/SRP.js +++ b/frontend/gamma/js/Clipperz/Crypto/SRP.js @@ -44,6 +44,8 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, { '_n': null, '_g': null, + '_k': null, + //------------------------------------------------------------------------- 'n': function() { @@ -64,6 +66,15 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, { return Clipperz.Crypto.SRP._g; }, + 'k': function() { + if (Clipperz.Crypto.SRP._k == null) { +// Clipperz.Crypto.SRP._k = new Clipperz.Crypto.BigInt(this.stringHash(this.n().asString() + this.g().asString()), 16); + Clipperz.Crypto.SRP._k = new Clipperz.Crypto.BigInt("64398bff522814e306a97cb9bfc4364b7eed16a8c17c5208a40a2bad2933c8e", 16); + } + + return Clipperz.Crypto.SRP._k; + }, + //----------------------------------------------------------------------------- 'exception': { @@ -138,10 +149,9 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'A': function () { if (this._A == null) { - // Warning: this value should be strictly greater than zero: how should we perform this check? + // Warning: this value should be strictly greater than zero this._A = Clipperz.Crypto.SRP.g().powerModule(this.a(), Clipperz.Crypto.SRP.n()); - - if (this._A.equals(0)) { + if (this._A.equals(0) || negative(this._A)) { Clipperz.logError("Clipperz.Crypto.SRP.Connection: trying to set 'A' to 0."); throw Clipperz.Crypto.SRP.exception.InvalidValue; } @@ -167,10 +177,9 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { }, 'set_B': function(aValue) { - // Warning: this value should be strictly greater than zero: how should we perform this check? - if (! aValue.equals(0)) { - this._B = aValue; - } else { + // Warning: this value should be strictly greater than zero + this._B = aValue; + if (this._B.equals(0) || negative(this._B)) { Clipperz.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to 0."); throw Clipperz.Crypto.SRP.exception.InvalidValue; } @@ -190,7 +199,7 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'u': function () { if (this._u == null) { - this._u = new Clipperz.Crypto.BigInt(this.stringHash(this.B().asString()), 16); + this._u = new Clipperz.Crypto.BigInt(this.stringHash(this.A().asString() + this.B().asString()), 16); } return this._u; @@ -207,9 +216,15 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { srp = Clipperz.Crypto.SRP; this._S = bigint.powerModule( - bigint.subtract(this.B(), bigint.powerModule(srp.g(), this.x(), srp.n())), - bigint.add(this.a(), bigint.multiply(this.u(), this.x())), - srp.n() + bigint.subtract( + this.B(), + bigint.multiply( + Clipperz.Crypto.SRP.k(), + bigint.powerModule(srp.g(), this.x(), srp.n()) + ) + ), + bigint.add(this.a(), bigint.multiply(this.u(), this.x())), + srp.n() ) } @@ -230,7 +245,20 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'M1': function () { if (this._M1 == null) { - this._M1 = this.stringHash(this.A().asString(10) + this.B().asString(10) + this.K()); +// this._M1 = this.stringHash(this.A().asString(10) + this.B().asString(10) + this.K()); + + // http://srp.stanford.edu/design.html + // User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K) + + this._M1 = this.stringHash( + "597626870978286801440197562148588907434001483655788865609375806439877501869636875571920406529" + + this.stringHash(this.C()) + + this.s().asString() + + this.A().asString() + + this.B().asString() + + this.K() + ); +//console.log("M1", this._M1); } return this._M1; @@ -241,6 +269,7 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'M2': function () { if (this._M2 == null) { this._M2 = this.stringHash(this.A().asString(10) + this.M1() + this.K()); +//console.log("M2", this._M2); } return this._M2; diff --git a/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js b/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js index b806cb7..e5f68a8 100644 --- a/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js +++ b/frontend/gamma/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js @@ -36,7 +36,7 @@ Clipperz.PM.Proxy.Offline.DataStore = function(args) { this._tolls = {}; this._currentStaticConnection = null; - + return this; } @@ -291,14 +291,14 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { throw "user already exists"; } } else { - throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly; + throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly; } result = { result: { 'lock': this.data()['users'][someParameters['credentials']['C']]['lock'], 'result': 'done' - }, + }, toll: this.getTollForRequestType('CONNECT') } @@ -329,7 +329,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { randomBytes = Clipperz.Crypto.Base.generateRandomSeed(); aConnection['b'] = new Clipperz.Crypto.BigInt(randomBytes, 16); v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16); - aConnection['B'] = v.add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n())); + aConnection['B'] = (Clipperz.Crypto.SRP.k().multiply(v)).add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n())); aConnection['A'] = someParameters.parameters.A; @@ -338,20 +338,35 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { nextTollRequestType = 'CONNECT'; } else if (someParameters.message == "credentialCheck") { - var v, u, S, A, K, M1; - + var v, u, s, S, A, K, M1; + var stringHash = function (aValue) { + return Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aValue)).toHexString().substring(2); + }; + v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16); - u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aConnection['B'].asString(10))).toHexString(), 16); A = new Clipperz.Crypto.BigInt(aConnection['A'], 16); + u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + aConnection['B'].asString(10))).toHexString(), 16); + s = new Clipperz.Crypto.BigInt(aConnection['userData']['s'], 16); S = (A.multiply(v.powerModule(u, Clipperz.Crypto.SRP.n()))).powerModule(aConnection['b'], Clipperz.Crypto.SRP.n()); - K = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(S.asString(10))).toHexString().slice(2); + K = stringHash(S.asString(10)); - M1 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + aConnection['B'].asString(10) + K)).toHexString().slice(2); + M1 = stringHash( + "597626870978286801440197562148588907434001483655788865609375806439877501869636875571920406529" + + stringHash(aConnection['C']) + + s.asString(10) + + A.asString(10) + + aConnection['B'].asString(10) + + K + ); if (someParameters.parameters.M1 == M1) { var M2; - M2 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + someParameters.parameters.M1 + K)).toHexString().slice(2); + M2 = stringHash( + A.asString(10) + + someParameters.parameters.M1 + + K + ); result['M2'] = M2; } else { throw new Error("Client checksum verification failed! Expected <" + M1 + ">, received <" + someParameters.parameters.M1 + ">.", "Error"); -- cgit v0.9.0.2