summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzul <azul@leap.se>2012-08-03 20:37:11 +0200
committerAzul <azul@leap.se>2012-08-03 20:37:11 +0200
commit94d1938e2e5d0ee5e8e7b9a8ed44a067677e0133 (patch)
treeffdae863dda3b197d6e165bffa5a782e9e1ab338
parent0da31f678580649415cb0487c830f21d7e163253 (diff)
moved all xhr related stuff to a seperate class
We can replace this if we want to use jquery ajax or similar. Also this has all the urls so it's super easy to overwrite
-rw-r--r--lib/plainXHR.js124
-rw-r--r--lib/srp.js173
-rw-r--r--lib/srp_register.js22
-rw-r--r--spec/DjangoSpecRunner.html1
-rw-r--r--spec/signup.js4
5 files changed, 173 insertions, 151 deletions
diff --git a/lib/plainXHR.js b/lib/plainXHR.js
new file mode 100644
index 0000000..44ee5df
--- /dev/null
+++ b/lib/plainXHR.js
@@ -0,0 +1,124 @@
+plainXHR = function() {
+
+ function getUrl()
+ {
+ return "";
+ }
+
+ function paths(path)
+ {
+ return path
+ }
+
+ // Perform ajax requests at the specified path, with the specified parameters
+ // Calling back the specified function.
+ function ajaxRequest(relative_path, params, callback)
+ {
+ var full_url = this.geturl() + this.paths(relative_path);
+ if( window.XMLHttpRequest)
+ xhr = new XMLHttpRequest();
+ else if (window.ActiveXObject){
+ try{
+ xhr = new ActiveXObject("Microsoft.XMLHTTP");
+ }catch (e){}
+ }
+ else
+ {
+ session.error_message("Ajax not supported.");
+ return;
+ }
+ if(xhr){
+ xhr.onreadystatechange = function() {
+ if(xhr.readyState == 4 && xhr.status == 200) {
+ callback(parseResponse());
+ }
+ };
+ xhr.open("POST", full_url, true);
+ xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ xhr.setRequestHeader("Content-length", params.length);
+ xhr.send(params);
+ }
+ else
+ {
+ session.error_message("Ajax failed.");
+ }
+ };
+
+ function parseResponse() {
+ if (responseIsXML()) {
+ return parseXML(xhr.responseXML);
+ } else if (responseIsJSON()) {
+ return JSON.parse(xhr.responseText);
+ }
+ };
+
+ function responseIsXML() {
+ return (xhr.responseType == 'document') ||
+ (xhr.getResponseHeader("Content-Type").indexOf('application/xml') >= 0)
+ }
+
+ function responseIsJSON() {
+ return (xhr.responseType == 'json') ||
+ (xhr.getResponseHeader("Content-Type").indexOf('application/json') >= 0)
+ }
+
+ function parseXML(xml) {
+ if (xml.getElementsByTagName("r").length > 0) {
+ return parseAttributesOfElement(xml.getElementsByTagName("r")[0]);
+ } else {
+ return parseNodes(xml.childNodes);
+ }
+ };
+
+ function parseAttributesOfElement(elem) {
+ var response = {};
+ for (var i = 0; i < elem.attributes.length; i++) {
+ var attrib = elem.attributes[i];
+ if (attrib.specified) {
+ response[attrib.name] = attrib.value;
+ }
+ }
+ return response;
+ };
+
+ function parseNodes(nodes) {
+ var response = {};
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ response[node.tagName] = node.textContent || true;
+ }
+ return response;
+ };
+
+ function register(I, callback)
+ {
+ this.ajaxRequest("register/salt/", "I="+I, callback);
+ }
+
+ function sendVerifier(v, callback) {
+ this.ajaxRequest("register/user/", "v="+v, callback);
+ }
+
+ function handshake(I, Astr, callback) {
+ this.ajaxRequest("handshake/", "I="+I+"&A="+Astr, callback);
+ }
+
+ function authenticate(M, callback) {
+ this.ajaxRequest("authenticate/", "M="+M, callback);
+ }
+
+ function upgrade(M, callback) {
+ this.ajaxRequest("upgrade/authenticate/", "M="+M, callback);
+ }
+
+ return {
+ geturl: getUrl,
+ paths: paths,
+ ajaxRequest: ajaxRequest,
+ register: register,
+ register_send_verifier: sendVerifier,
+ handshake: handshake,
+ authenticate: authenticate,
+ upgrade: upgrade
+ }
+}
diff --git a/lib/srp.js b/lib/srp.js
index 0f6889b..9ef75f5 100644
--- a/lib/srp.js
+++ b/lib/srp.js
@@ -1,6 +1,6 @@
function SRP()
{
- // Variables that will be used in the SRP protocol
+ // Variables session will be used in the SRP protocol
var Nstr = "115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3";
var N = new BigInteger(Nstr, 16);
var g = new BigInteger("2");
@@ -18,11 +18,11 @@ function SRP()
var K = null;
var M = null;
var M2 = null;
- var that = this;
+ var session = this;
var authenticated = false;
var I = document.getElementById("srp_username").value;
var p = document.getElementById("srp_password").value;
- var xhr = null;
+ var remote = plainXHR();
// *** Accessor methods ***
@@ -47,18 +47,6 @@ function SRP()
return new BigInteger(64, rng).toString(16);
}
- // Returns the XMLHttpRequest object
- this.getxhr = function()
- {
- return xhr;
- };
-
- // Returns the base URL - overwrite to use a different one
- this.geturl = function()
- {
- return "";
- };
-
// Returns the BigInteger, g
this.getg = function()
{
@@ -82,129 +70,43 @@ function SRP()
return this.getg().modPow(this.calcX(salt), this.getN());
}
- // Overwrite this if you want to change the paths
- this.paths = function(str)
- {
- return str;
- };
-
// Check whether or not a variable is defined
function isdefined ( variable)
{
return (typeof(window[variable]) != "undefined");
};
-
+
// *** Actions ***
- // Perform ajax requests at the specified url, with the specified parameters
- // Calling back the specified function.
- this.ajaxRequest = function(full_url, params, callback)
- {
- if( window.XMLHttpRequest)
- xhr = new XMLHttpRequest();
- else if (window.ActiveXObject){
- try{
- xhr = new ActiveXObject("Microsoft.XMLHTTP");
- }catch (e){}
- }
- else
- {
- that.error_message("Ajax not supported.");
- return;
- }
- if(xhr){
- xhr.onreadystatechange = function() {
- if(xhr.readyState == 4 && xhr.status == 200) {
- callback(parseResponse());
- }
- };
- xhr.open("POST", full_url, true);
- xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
- xhr.setRequestHeader("Content-length", params.length);
- xhr.send(params);
- }
- else
- {
- that.error_message("Ajax failed.");
- }
- };
-
- function parseResponse() {
- if (responseIsXML()) {
- return parseXML(xhr.responseXML);
- } else if (responseIsJSON()) {
- return JSON.parse(xhr.responseText);
- }
- };
-
- function responseIsXML() {
- return (xhr.responseType == 'document') ||
- (xhr.getResponseHeader("Content-Type").indexOf('application/xml') >= 0)
- }
-
- function responseIsJSON() {
- return (xhr.responseType == 'json') ||
- (xhr.getResponseHeader("Content-Type").indexOf('application/json') >= 0)
- }
-
- function parseXML(xml) {
- if (xml.getElementsByTagName("r").length > 0) {
- return parseAttributesOfElement(xml.getElementsByTagName("r")[0]);
- } else {
- return parseNodes(xml.childNodes);
- }
- };
-
- function parseAttributesOfElement(elem) {
- var response = {};
- for (var i = 0; i < elem.attributes.length; i++) {
- var attrib = elem.attributes[i];
- if (attrib.specified) {
- response[attrib.name] = attrib.value;
- }
- }
- return response;
- };
-
- function parseNodes(nodes) {
- var response = {};
- for (var i = 0; i < nodes.length; i++) {
- var node = nodes[i];
- response[node.tagName] = node.textContent || true;
- }
- return response;
- };
-
// Start the login process by identifying the user
this.identify = function()
{
- var handshake_url = that.geturl() + that.paths("handshake/");
- var params = "I="+I+"&A="+Astr;
- that.ajaxRequest(handshake_url, params, receive_salts);
+ this.remote.handshake(I, Astr, receive_salts);
};
// Receive login salts from the server, start calculations
function receive_salts(response)
{
if(response.error) {
- that.error_message(response.error);
- }
- // B = 0 will make the algorithm always succeed - refuse such a server
- // answer
+ session.error_message(response.error);
+ }
+ // B = 0 will make the algorithm always succeed - refuse such a server
+ // answer
else if(response.B == 0) {
- that.error_message("Server send random number 0 - this is not allowed");
+ session.error_message("Server send random number 0 - this is not allowed");
}
// If there is no algorithm specified, calculate M given s, B, and P
else if(!response.a)
{
calculations(response.s, response.B, p);
- that.ajaxRequest(that.geturl()+that.paths("authenticate/"), "M="+M, confirm_authentication);
+ remote.authenticate(M, confirm_authentication)
}
// If there is an algorithm specified, start the login process
else {
upgrade(response.s, response.B, response.a, response.d);
}
};
+
// Calculate S, M, and M2
// This is the client side of the SRP specification
function calculations(s, ephemeral, pass)
@@ -235,13 +137,13 @@ function SRP()
if(response.M == M2)
{
authenticated = true;
- that.success();
+ session.success();
}
else
- that.error_message("Server key does not match");
+ session.error_message("Server key does not match");
}
else if (response.error)
- that.error_message(response.error);
+ session.error_message(response.error);
};
// *** Upgrades ***
@@ -267,7 +169,7 @@ function SRP()
hashfun = MD5;
//alert(hashfun(dsalt+p));
calculations(s, ephemeral, hashfun(dsalt+p));
- that.ajaxRequest(that.geturl()+that.paths("upgrade/authenticate/"), "M="+M, confirm_upgrade);
+ remote.upgrade(M, session.confirm_upgrade)
};
window.setTimeout(do_upgrade,10);
};
@@ -275,7 +177,7 @@ function SRP()
// Encrypt plaintext using slowAES
function encrypt(plaintext)
{
- var key = cryptoHelpers.toNumbers(that.key());
+ var key = cryptoHelpers.toNumbers(session.key());
var byteMessage = cryptoHelpers.convertStringToByteArray(plaintext);
var iv = new Array(16);
rng.nextBytes(iv);
@@ -287,7 +189,7 @@ function SRP()
return retstring;
};
- // Receive the server's M, confirming that the server has HASH(p)
+ // Receive the server's M, confirming session the server has HASH(p)
// Next, send P in plaintext (this is the **only** time it should ever be sent plain text)
function confirm_upgrade(response)
{
@@ -296,26 +198,26 @@ function SRP()
if(response.M == M2)
{
K = SHA256(S.toString(16));
- var auth_url = that.geturl() + that.paths("upgrade/verifier/");
- that.ajaxRequest(auth_url, "p="+encrypt(p)+"&l="+p.length, confirm_verifier);
+ var auth_url = session.geturl() + session.paths("upgrade/verifier/");
+ session.ajaxRequest(auth_url, "p="+encrypt(p)+"&l="+p.length, confirm_verifier);
}
else
- that.error_message("Server key does not match");
+ session.error_message("Server key does not match");
}
else if (response.error)
{
- that.error_message(response.error);
+ session.error_message(response.error);
}
};
- // After sending the password, check that the response is OK, then reidentify
+ // After sending the password, check session the response is OK, then reidentify
function confirm_verifier(response)
{
K = null;
if(response.ok)
- that.identify();
+ session.identify();
else
- that.error_message("Verifier could not be confirmed");
+ session.error_message("Verifier could not be confirmed");
};
// This loads javascript libraries. Fname is the path to the library to be imported
@@ -329,10 +231,10 @@ function SRP()
// If we need SHA1 or MD5, we need to load the javascript files
function import_hashes()
{
- // First check that the functions aren't already loaded
+ // First check session the functions aren't already loaded
if(isdefined("SHA1") && isdefined("MD5")) return;
- // Get the directory that this javascript file was loaded from
- var arr=that.srpPath.split("/");
+ // Get the directory session this javascript file was loaded from
+ var arr=session.srpPath.split("/");
var path = arr.slice(0, arr.length-1).join("/");
// If this file is called srp.min.js, we will load the packed hash file
if(arr[arr.length-1] == "srp.min.js")
@@ -372,18 +274,23 @@ function SRP()
return K;
}
else
- that.error_message("User has not been authenticated.");
+ session.error_message("User has not been authenticated.");
else
return K;
};
- // If an error occurs, raise it as an alert.
- // Developers can set this to an alternative function to handle erros differently.
- this.error_message = function(t)
- {
- alert(t);
- };
+ // If an error occurs, raise it as an alert.
+ // Developers can set this to an alternative function to handle erros differently.
+ this.error_message = function(t)
+ {
+ alert(t);
};
+
+
+ // exposing the remote handler so it can be modified
+ this.remote = remote;
+
+};
// This line is run while the document is loading
// It gets a list of all <script> tags and finds the last instance.
// The path to this script is the "src" attribute of that tag.
diff --git a/lib/srp_register.js b/lib/srp_register.js
index 1666476..8365fed 100644
--- a/lib/srp_register.js
+++ b/lib/srp_register.js
@@ -5,10 +5,8 @@ function SRP_REGISTER()
// Initiate the registration process
SRP.prototype.register = function()
{
- that = this;
- var handshake_url = this.geturl() + this.paths("register/salt/");
- var params = "I="+this.getI();
- this.ajaxRequest(handshake_url, params, this.register_receive_salt);
+ session = this;
+ this.remote.register(this.getI(), session.register_receive_salt);
};
// Receive the salt for registration
@@ -17,28 +15,20 @@ function SRP_REGISTER()
if(response.salt)
{
var s = response.salt;
- var v = that.calcV(s);
- that.register_send_verifier(v.toString(16));
+ var v = session.calcV(s);
+ session.remote.register_send_verifier(v.toString(16), session.registered_user);
}
else if(response.error)
{
- that.error_message(response.error);
+ session.error_message(response.error);
}
};
- // Send the verifier to the server
- SRP.prototype.register_send_verifier = function(v)
- {
- var params = "v="+v;
- var auth_url = that.geturl() + that.paths("register/user/");
- that.ajaxRequest(auth_url, params, that.registered_user);
- };
-
// The user has been registered successfully, now login
SRP.prototype.registered_user = function(response)
{
if(response.ok)
{
- that.identify();
+ session.identify();
}
};
};
diff --git a/spec/DjangoSpecRunner.html b/spec/DjangoSpecRunner.html
index c818392..8315c5b 100644
--- a/spec/DjangoSpecRunner.html
+++ b/spec/DjangoSpecRunner.html
@@ -20,6 +20,7 @@
<script type="text/javascript" src="../lib/jsbn.js"></script>
<script type="text/javascript" src="../lib/jsbn2.js"></script>
<script type="text/javascript" src="../lib/srp.js"></script>
+ <script type="text/javascript" src="../lib/plainXHR.js"></script>
<script type="text/javascript" src="../lib/srp_register.js"></script>
<!-- include spec files here... -->
diff --git a/spec/signup.js b/spec/signup.js
index b5099b8..b38778a 100644
--- a/spec/signup.js
+++ b/spec/signup.js
@@ -24,11 +24,11 @@ describe("Signup", function() {
it("receives the salt from /register/salt", function(){
var callback = sinon.spy();
- this.srp.register_send_verifier = callback;
+ this.srp.remote.register_send_verifier = callback;
this.srp.register();
this.expectRequest('register/salt/', "I=user")
this.respondXML("<salt>5d3055e0acd3ddcfc15</salt>");
- expect(callback).toHaveBeenCalledWith("adcd57b4a4a05c2e205b0b7b30014d9ff635d8d8db2f502f08e9b9c132800c44");
+ expect(callback).toHaveBeenCalledWith("adcd57b4a4a05c2e205b0b7b30014d9ff635d8d8db2f502f08e9b9c132800c44", this.srp.registered_user);
});
it("identifies after successful registration (INTEGRATION)", function(){