-rw-r--r-- | README.md | 32 | ||||
-rw-r--r-- | frontend/beta/js/Clipperz/Base.js | 28 | ||||
-rw-r--r-- | frontend/beta/js/Clipperz/Crypto/PRNG.js | 130 | ||||
-rw-r--r-- | frontend/beta/js/Clipperz/Crypto/SRP.js | 67 | ||||
-rw-r--r-- | frontend/beta/js/Clipperz/PM/BookmarkletProcessor.js | 2 | ||||
-rw-r--r-- | frontend/beta/js/Clipperz/PM/Components/RecordDetail/DirectLoginBindingComponent.js | 4 | ||||
-rw-r--r-- | frontend/beta/js/Clipperz/PM/DataModel/DirectLogin.js | 22 | ||||
-rw-r--r-- | frontend/beta/js/Clipperz/PM/DataModel/DirectLoginReference.js | 2 | ||||
-rw-r--r-- | frontend/beta/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js | 47 | ||||
-rw-r--r-- | frontend/delta/js/Clipperz/Crypto/PRNG.js | 128 | ||||
-rw-r--r-- | frontend/delta/js/Clipperz/Crypto/SRP.js | 53 | ||||
-rw-r--r-- | frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js | 27 | ||||
-rw-r--r-- | frontend/gamma/js/Clipperz/Crypto/PRNG.js | 128 | ||||
-rw-r--r-- | frontend/gamma/js/Clipperz/Crypto/SRP.js | 53 | ||||
-rw-r--r-- | frontend/gamma/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js | 35 |
15 files changed, 407 insertions, 351 deletions
@@ -8,48 +8,50 @@ Clipperz is an online vault where you can store confidential data without worryi Since passwords are the most common type of private information that you need to protect, we have added a great deal of functionality to make Clipperz a great [online password manager][home] thus solving the “password fatigue” problem. **Clipperz makes the Internet the most convenient and safe place to keep you most precious and sensitive data.** 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 If you like what Clipperz is building, its openness and its view of cryptography as a powerful tool for liberty, then you may consider making a donation. Our favorite payment method is clearly Bitcoin ([learn why here][why]), but you can also send your donation via credit card, Paypal or bank transfer. In all cases there will be no link between your real identity and your encrypted data stored on Clipperz. **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] This enables a single entity to represent the aggregated code base and gives the community flexibility to act as a whole to changing situations. The CA establishes a joint copyright assignment in which the contributor retains copyright ownership while also granting those rights to Clipperz Srl. With the CA in place, the aggregated code base within any Clipperz open source project is protected by both the distribution license and copyright law. @@ -112,7 +114,7 @@ Once the index.html files have been built (one for each frontend) and a backend ## Disclaimer 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 @@ -243,12 +243,40 @@ MochiKit.Base.update(Clipperz.Base, { result = aValue; } 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': { 'AbstractMethod': new MochiKit.Base.NamedError("Clipperz.Base.exception.AbstractMethod"), 'UnknownType': new MochiKit.Base.NamedError("Clipperz.Base.exception.UnknownType"), 'VulnerabilityIssue': new MochiKit.Base.NamedError("Clipperz.Base.exception.VulnerabilityIssue") 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 @@ -194,18 +194,12 @@ Clipperz.Crypto.PRNG.TimeRandomnessSource.prototype = MochiKit.Base.update(new C 'numberOfRandomBits': function() { return 5; }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //***************************************************************************** Clipperz.Crypto.PRNG.MouseRandomnessSource = function(args) { @@ -242,18 +236,18 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new 'appendRandomBitsToRandomBitsCollector': function(aValue) { var collectedBits; 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); } this.setNumberOfRandomBitsCollected(numberOfRandomBitsCollected) }, @@ -286,102 +280,60 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new 'numberOfRandomBits': function() { return 1; }, //------------------------------------------------------------------------- - - '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" }); //############################################################################# Clipperz.Crypto.PRNG.Fortuna = function(args) { @@ -604,35 +556,29 @@ 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, 'callback'); result = deferredResult; } -//MochiKit.Logging.logDebug("<<< PRNG.deferredEntropyCollection - result: " + result); return result; }, //------------------------------------------------------------------------- @@ -640,13 +586,13 @@ MochiKit.Logging.logWarning("Fortuna generator has not enough entropy, yet!"); while (! this.isReadyToGenerateRandomValues()) { this.addRandomByte(Math.floor(Math.random() * 32), Math.floor(Math.random() * 32), Math.floor(Math.random() * 256)); } }, //------------------------------------------------------------------------- - +/* 'dump': function(appendToDoc) { var tbl; var i,c; tbl = document.createElement("table"); tbl.border = 0; @@ -746,13 +692,13 @@ MochiKit.Logging.logWarning("Fortuna generator has not enough entropy, yet!"); } ne.appendChild(tbl); } return tbl; }, - +*/ //----------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //############################################################################# @@ -821,22 +767,32 @@ Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { newRandomnessSource = new Clipperz.Crypto.PRNG.MouseRandomnessSource(); _clipperz_crypt_prng_defaultPRNG.addRandomnessSource(newRandomnessSource); } //............................................................. // - // 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 @@ -41,12 +41,14 @@ Clipperz.Crypto.SRP.NAME = "Clipperz.Crypto.SRP"; //############################################################################# MochiKit.Base.update(Clipperz.Crypto.SRP, { '_n': null, '_g': null, + '_k': null, + //------------------------------------------------------------------------- 'n': function() { if (Clipperz.Crypto.SRP._n == null) { Clipperz.Crypto.SRP._n = new Clipperz.Crypto.BigInt("115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16); } @@ -61,12 +63,21 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, { Clipperz.Crypto.SRP._g = new Clipperz.Crypto.BigInt(2); // eventually 5 (as suggested on the Diffi-Helmann documentation) } 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': { 'InvalidValue': new MochiKit.Base.NamedError("Clipperz.Crypto.SRP.exception.InvalidValue") }, @@ -126,40 +137,36 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { //------------------------------------------------------------------------- 'a': function () { 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; }, //------------------------------------------------------------------------- '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; }, //------------------------------------------------------------------------- 's': function () { return this._s; -//MochiKit.Logging.logDebug("SRP s: " + this._S); }, 'set_s': function(aValue) { this._s = aValue; }, @@ -167,39 +174,35 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'A' to 'B': function () { return this._B; }, '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; } }, //------------------------------------------------------------------------- '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; }, //------------------------------------------------------------------------- '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; }, //------------------------------------------------------------------------- @@ -210,50 +213,66 @@ MochiKit.Logging.logError("Clipperz.Crypto.SRP.Connection: trying to set 'B' to var srp; bigint = Clipperz.Crypto.BigInt; 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; }, //------------------------------------------------------------------------- 'K': function () { if (this._K == null) { this._K = this.stringHash(this.S().asString()); -//MochiKit.Logging.logDebug("SRP K: " + this._K); } return this._K; }, //------------------------------------------------------------------------- '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; }, //------------------------------------------------------------------------- '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 @@ -135,13 +135,13 @@ Clipperz.PM.BookmarkletProcessor.prototype = MochiKit.Base.update(null, { //------------------------------------------------------------------------- 'hostname': function() { 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'); } return this._hostname; }, 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 @@ -97,13 +97,13 @@ YAHOO.extendX(Clipperz.PM.Components.RecordDetail.DirectLoginBindingComponent, C recordFields = this.directLoginBinding().directLogin().record().currentVersion().fields(); result = []; option = {tag:'option', value:null, html:'---'}; result.push(option); for (recordFieldKey in recordFields) { // TODO: remove the value: field and replace it with element.dom.value = <some 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; } result.push(option); } //MochiKit.Logging.logDebug("<<< DirectLoginBindingComponent.recordFieldOptions"); @@ -147,13 +147,13 @@ YAHOO.extendX(Clipperz.PM.Components.RecordDetail.DirectLoginBindingComponent, C 'updateViewMode': function() { //MochiKit.Logging.logDebug(">>> DirectLoginBindingComponent.updateViewMode"); 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"); }, //------------------------------------------------------------------------- 'synchronizeComponentValues': function() { 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 @@ -35,13 +35,13 @@ Clipperz.PM.DataModel.DirectLogin = function(args) { args = args || {}; //MochiKit.Logging.logDebug("--- new Clipperz.PM.DataModel.DirectLogin - args: " + Clipperz.Base.serializeJSON(MochiKit.Base.keys(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; this._formValues = args.formValues || {}; this.setFormData(args.formData || null); @@ -99,15 +99,15 @@ Clipperz.PM.DataModel.DirectLogin.prototype = MochiKit.Base.update(null, { 'favicon': function() { if (this._favicon == 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; }, //------------------------------------------------------------------------- @@ -134,12 +134,20 @@ Clipperz.PM.DataModel.DirectLogin.prototype = MochiKit.Base.update(null, { }, 'setFixedFavicon': function(aValue) { this._fixedFavicon = aValue; }, + 'action': function () { + var result; + + result = Clipperz.Base.sanitizeUrl(this.formData()['attributes']['action']); + + return result; + }, + //------------------------------------------------------------------------- 'bookmarkletVersion': function() { return this._bookmarkletVersion; }, @@ -439,13 +447,13 @@ Clipperz.PM.DataModel.DirectLogin.prototype = MochiKit.Base.update(null, { // MochiKit.DOM.currentDocument().write('<html><head><title>' + this.label() + '</title><META http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body></body></html>') //MochiKit.Logging.logDebug("### runDirectLogin - 3.1"); MochiKit.DOM.appendChildNodes(MochiKit.DOM.currentDocument().body, MochiKit.DOM.H3(null, "Loading " + this.label() + " ...")); //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"); MochiKit.DOM.appendChildNodes(MochiKit.DOM.currentDocument().body, MochiKit.DOM.DIV({style:'display:none; visibility:hidden;'}, formElement) @@ -484,15 +492,15 @@ Clipperz.PM.DataModel.DirectLogin.prototype = MochiKit.Base.update(null, { 'runDirectLogin': function(aNewWindow) { var newWindow; //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') { aNewWindow.close(); } 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 @@ -44,13 +44,13 @@ Clipperz.PM.DataModel.DirectLoginReference = function(args) { this._directLogin = args.directLogin; this._record = args.directLogin.record(); } else { 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; } this._fixedFavicon = 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 @@ -34,12 +34,13 @@ Clipperz.PM.Proxy.Offline.DataStore = function(args) { this._isReadOnly = (typeof(args.readOnly) == 'undefined' ? true : args.readOnly); this._shouldPayTolls = args.shouldPayTolls || false; this._tolls = {}; this._connections = {}; + this._C = null; this._b = null; this._B = null; this._A = null; this._userData = null; return this; @@ -141,12 +142,22 @@ Clipperz.PM.Proxy.Offline.DataStore.prototype = MochiKit.Base.update(null, { return deferredResult; }, //========================================================================= + 'C': function() { + return this._C; + }, + + 'set_C': function(aValue) { + this._C = aValue; + }, + + //------------------------------------------------------------------------- + 'b': function() { return this._b; }, 'set_b': function(aValue) { this._b = aValue; @@ -233,14 +244,14 @@ Clipperz.PM.Proxy.Offline.DataStore.prototype = MochiKit.Base.update(null, { throw "Missing toll"; } } }, //========================================================================= - - 'processMessage': function(aFunctionName, someParameters) { + + 'processMessage': function (aFunctionName, someParameters) { var result; switch(aFunctionName) { case 'knock': result = this._knock(someParameters); break; @@ -300,20 +311,20 @@ Clipperz.PM.Proxy.Offline.DataStore.prototype = MochiKit.Base.update(null, { 'records': {} } } else { 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') } return MochiKit.Async.succeed(result); }, @@ -337,38 +348,54 @@ Clipperz.PM.Proxy.Offline.DataStore.prototype = MochiKit.Base.update(null, { this.setUserData(userData); } else { this.setUserData(this.data()['users']['catchAllUser']); } 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); result['s'] = this.userData()['s']; result['B'] = this.B().asString(16); 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"); } nextTollRequestType = 'MESSAGE'; 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 @@ -18,12 +18,14 @@ refer to http://www.clipperz.com. * You should have received a copy of the GNU Affero General Public License along with Clipperz. If not, see http://www.gnu.org/licenses/. */ +"use strict"; + try { if (typeof(Clipperz.ByteArray) == 'undefined') { throw ""; }} catch (e) { throw "Clipperz.Crypto.PRNG depends on Clipperz.ByteArray!"; } try { if (typeof(Clipperz.Crypto.SHA) == 'undefined') { throw ""; }} catch (e) { throw "Clipperz.Crypto.PRNG depends on Clipperz.Crypto.SHA!"; @@ -194,18 +196,12 @@ Clipperz.Crypto.PRNG.TimeRandomnessSource.prototype = MochiKit.Base.update(new C 'numberOfRandomBits': function() { return 5; }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //***************************************************************************** Clipperz.Crypto.PRNG.MouseRandomnessSource = function(args) { @@ -242,18 +238,18 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new 'appendRandomBitsToRandomBitsCollector': function(aValue) { var collectedBits; 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); } this.setNumberOfRandomBitsCollected(numberOfRandomBitsCollected) }, @@ -286,102 +282,60 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new 'numberOfRandomBits': function() { return 1; }, //------------------------------------------------------------------------- - - '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" }); //############################################################################# Clipperz.Crypto.PRNG.Fortuna = function(args) { @@ -632,13 +586,13 @@ Clipperz.logWarning("Fortuna generator has not enough entropy, yet!"); while (! this.isReadyToGenerateRandomValues()) { this.addRandomByte(Math.floor(Math.random() * 32), Math.floor(Math.random() * 32), Math.floor(Math.random() * 256)); } }, //------------------------------------------------------------------------- - +/* 'dump': function(appendToDoc) { var tbl; var i,c; tbl = document.createElement("table"); tbl.border = 0; @@ -738,13 +692,13 @@ Clipperz.logWarning("Fortuna generator has not enough entropy, yet!"); } ne.appendChild(tbl); } return tbl; }, - +*/ //----------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //############################################################################# @@ -781,13 +735,13 @@ Clipperz.Crypto.PRNG.Random.prototype = MochiKit.Base.update(null, { //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //############################################################################# -_clipperz_crypt_prng_defaultPRNG = null; +var _clipperz_crypt_prng_defaultPRNG = null; Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { if (_clipperz_crypt_prng_defaultPRNG == null) { _clipperz_crypt_prng_defaultPRNG = new Clipperz.Crypto.PRNG.Fortuna(); //............................................................. @@ -813,22 +767,32 @@ Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { newRandomnessSource = new Clipperz.Crypto.PRNG.MouseRandomnessSource(); _clipperz_crypt_prng_defaultPRNG.addRandomnessSource(newRandomnessSource); } //............................................................. // - // 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 @@ -41,12 +41,14 @@ Clipperz.Crypto.SRP.NAME = "Clipperz.Crypto.SRP"; //############################################################################# MochiKit.Base.update(Clipperz.Crypto.SRP, { '_n': null, '_g': null, + '_k': null, + //------------------------------------------------------------------------- 'n': function() { if (Clipperz.Crypto.SRP._n == null) { Clipperz.Crypto.SRP._n = new Clipperz.Crypto.BigInt("115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16); } @@ -61,12 +63,21 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, { Clipperz.Crypto.SRP._g = new Clipperz.Crypto.BigInt(2); // eventually 5 (as suggested on the Diffi-Helmann documentation) } 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': { 'InvalidValue': new MochiKit.Base.NamedError("Clipperz.Crypto.SRP.exception.InvalidValue") }, @@ -135,16 +146,15 @@ 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; } } return this._A; @@ -164,16 +174,15 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'B': function () { return this._B; }, '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; } }, //------------------------------------------------------------------------- @@ -187,13 +196,13 @@ 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; }, //------------------------------------------------------------------------- @@ -204,15 +213,21 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { var srp; bigint = Clipperz.Crypto.BigInt; 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() ) } return this._S; }, @@ -227,23 +242,37 @@ 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; }, //------------------------------------------------------------------------- '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 @@ -85,35 +85,50 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.LocalStorageDataStore, Clipperz.P aConnection['userData'] = this.data()['users']['catchAllUser']; } 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; result['s'] = aConnection['userData']['s']; result['B'] = aConnection['B'].asString(16); 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"); } nextTollRequestType = 'MESSAGE'; 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 @@ -18,12 +18,14 @@ refer to http://www.clipperz.com. * You should have received a copy of the GNU Affero General Public License along with Clipperz. If not, see http://www.gnu.org/licenses/. */ +"use strict"; + try { if (typeof(Clipperz.ByteArray) == 'undefined') { throw ""; }} catch (e) { throw "Clipperz.Crypto.PRNG depends on Clipperz.ByteArray!"; } try { if (typeof(Clipperz.Crypto.SHA) == 'undefined') { throw ""; }} catch (e) { throw "Clipperz.Crypto.PRNG depends on Clipperz.Crypto.SHA!"; @@ -194,18 +196,12 @@ Clipperz.Crypto.PRNG.TimeRandomnessSource.prototype = MochiKit.Base.update(new C 'numberOfRandomBits': function() { return 5; }, //------------------------------------------------------------------------- - - 'pollingFrequency': function() { - return 10; - }, - - //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //***************************************************************************** Clipperz.Crypto.PRNG.MouseRandomnessSource = function(args) { @@ -242,18 +238,18 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new 'appendRandomBitsToRandomBitsCollector': function(aValue) { var collectedBits; 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); } this.setNumberOfRandomBitsCollected(numberOfRandomBitsCollected) }, @@ -286,102 +282,60 @@ Clipperz.Crypto.PRNG.MouseRandomnessSource.prototype = MochiKit.Base.update(new 'numberOfRandomBits': function() { return 1; }, //------------------------------------------------------------------------- - - '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" }); //############################################################################# Clipperz.Crypto.PRNG.Fortuna = function(args) { @@ -632,13 +586,13 @@ Clipperz.logWarning("Fortuna generator has not enough entropy, yet!"); while (! this.isReadyToGenerateRandomValues()) { this.addRandomByte(Math.floor(Math.random() * 32), Math.floor(Math.random() * 32), Math.floor(Math.random() * 256)); } }, //------------------------------------------------------------------------- - +/* 'dump': function(appendToDoc) { var tbl; var i,c; tbl = document.createElement("table"); tbl.border = 0; @@ -738,13 +692,13 @@ Clipperz.logWarning("Fortuna generator has not enough entropy, yet!"); } ne.appendChild(tbl); } return tbl; }, - +*/ //----------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //############################################################################# @@ -781,13 +735,13 @@ Clipperz.Crypto.PRNG.Random.prototype = MochiKit.Base.update(null, { //------------------------------------------------------------------------- __syntaxFix__: "syntax fix" }); //############################################################################# -_clipperz_crypt_prng_defaultPRNG = null; +var _clipperz_crypt_prng_defaultPRNG = null; Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { if (_clipperz_crypt_prng_defaultPRNG == null) { _clipperz_crypt_prng_defaultPRNG = new Clipperz.Crypto.PRNG.Fortuna(); //............................................................. @@ -813,22 +767,32 @@ Clipperz.Crypto.PRNG.defaultRandomGenerator = function() { newRandomnessSource = new Clipperz.Crypto.PRNG.MouseRandomnessSource(); _clipperz_crypt_prng_defaultPRNG.addRandomnessSource(newRandomnessSource); } //............................................................. // - // 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 @@ -41,12 +41,14 @@ Clipperz.Crypto.SRP.NAME = "Clipperz.Crypto.SRP"; //############################################################################# MochiKit.Base.update(Clipperz.Crypto.SRP, { '_n': null, '_g': null, + '_k': null, + //------------------------------------------------------------------------- 'n': function() { if (Clipperz.Crypto.SRP._n == null) { Clipperz.Crypto.SRP._n = new Clipperz.Crypto.BigInt("115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16); } @@ -61,12 +63,21 @@ MochiKit.Base.update(Clipperz.Crypto.SRP, { Clipperz.Crypto.SRP._g = new Clipperz.Crypto.BigInt(2); // eventually 5 (as suggested on the Diffi-Helmann documentation) } 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': { 'InvalidValue': new MochiKit.Base.NamedError("Clipperz.Crypto.SRP.exception.InvalidValue") }, @@ -135,16 +146,15 @@ 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; } } return this._A; @@ -164,16 +174,15 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { 'B': function () { return this._B; }, '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; } }, //------------------------------------------------------------------------- @@ -187,13 +196,13 @@ 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; }, //------------------------------------------------------------------------- @@ -204,15 +213,21 @@ Clipperz.Crypto.SRP.Connection.prototype = MochiKit.Base.update(null, { var srp; bigint = Clipperz.Crypto.BigInt; 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() ) } return this._S; }, @@ -227,23 +242,37 @@ 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; }, //------------------------------------------------------------------------- '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 @@ -33,13 +33,13 @@ Clipperz.PM.Proxy.Offline.DataStore = function(args) { this._data = args.data || (typeof(_clipperz_dump_data_) != 'undefined' ? _clipperz_dump_data_ : null); this._isReadOnly = (typeof(args.readOnly) == 'undefined' ? true : args.readOnly); this._shouldPayTolls = args.shouldPayTolls || false; this._tolls = {}; this._currentStaticConnection = null; - + return this; } Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { //------------------------------------------------------------------------- @@ -288,20 +288,20 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { 'records': {} } } else { 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') } return result; }, @@ -326,35 +326,50 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { aConnection['userData'] = this.data()['users']['catchAllUser']; } 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; result['s'] = aConnection['userData']['s']; result['B'] = aConnection['B'].asString(16); 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"); } nextTollRequestType = 'MESSAGE'; |