summaryrefslogtreecommitdiff
path: root/javascript/srp.js
diff options
context:
space:
mode:
Diffstat (limited to 'javascript/srp.js')
-rw-r--r--javascript/srp.js626
1 files changed, 313 insertions, 313 deletions
diff --git a/javascript/srp.js b/javascript/srp.js
index 9aa47b9..f2ee4d5 100644
--- a/javascript/srp.js
+++ b/javascript/srp.js
@@ -1,355 +1,355 @@
function SRP()
{
- // Variables that will be used in the SRP protocol
- var Nstr = "115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3";
- var N = new BigInteger(Nstr, 16);
- var g = new BigInteger("2");
- var k = new BigInteger("c46d46600d87fef149bd79b81119842f3c20241fda67d06ef412d8f6d9479c58", 16);
- var rng = new SecureRandom();
- var a = new BigInteger(32, rng);
- var A = g.modPow(a, N);
- while(A.mod(N) == 0)
- {
- a = new BigInteger(32, rng);
- A = g.modPow(a, N);
- }
- var Astr = A.toString(16);
- var S = null;
- var K = null;
- var M = null;
- var M2 = null;
- var url = document.getElementById("srp_url").value;
- var server = document.getElementById("srp_server").value;
- var that = this;
- var authenticated = false;
- var I = document.getElementById("srp_username").value;
- var p = document.getElementById("srp_password").value;
- var xhr = null;
+ // Variables that will be used in the SRP protocol
+ var Nstr = "115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3";
+ var N = new BigInteger(Nstr, 16);
+ var g = new BigInteger("2");
+ var k = new BigInteger("c46d46600d87fef149bd79b81119842f3c20241fda67d06ef412d8f6d9479c58", 16);
+ var rng = new SecureRandom();
+ var a = new BigInteger(32, rng);
+ var A = g.modPow(a, N);
+ while(A.mod(N) == 0)
+ {
+ a = new BigInteger(32, rng);
+ A = g.modPow(a, N);
+ }
+ var Astr = A.toString(16);
+ var S = null;
+ var K = null;
+ var M = null;
+ var M2 = null;
+ var url = document.getElementById("srp_url").value;
+ var server = document.getElementById("srp_server").value;
+ var that = this;
+ var authenticated = false;
+ var I = document.getElementById("srp_username").value;
+ var p = document.getElementById("srp_password").value;
+ var xhr = null;
- // *** Accessor methods ***
+ // *** Accessor methods ***
- // allows setting the random number A for testing
-
- this.calculateAndSetA = function(_a)
- {
- a = new BigInteger(_a, 16);
- A = g.modPow(a, N);
- Astr = A.toString(16);
- return Astr;
- };
+ // allows setting the random number A for testing
- // Returns the user's identity
- this.getI = function()
- {
- return I;
- };
+ this.calculateAndSetA = function(_a)
+ {
+ a = new BigInteger(_a, 16);
+ A = g.modPow(a, N);
+ Astr = A.toString(16);
+ return Astr;
+ };
- // Returns the XMLHttpRequest object
- this.getxhr = function()
- {
- return xhr;
- };
+ // Returns the user's identity
+ this.getI = function()
+ {
+ return I;
+ };
- // Returns the base URL
- this.geturl = function()
- {
- return url;
- };
- // Returns the BigInteger, g
- this.getg = function()
- {
- return g;
- };
+ // Returns the XMLHttpRequest object
+ this.getxhr = function()
+ {
+ return xhr;
+ };
- // Returns the BigInteger, N
- this.getN = function()
- {
- return N;
- };
+ // Returns the base URL
+ this.geturl = function()
+ {
+ return url;
+ };
+ // Returns the BigInteger, g
+ this.getg = function()
+ {
+ return g;
+ };
- // Calculates the X value and return it as a BigInteger
- this.calcX = function(s)
- {
- return new BigInteger(SHA256(s + SHA256(I + ":" + p)), 16);
- };
+ // Returns the BigInteger, N
+ this.getN = function()
+ {
+ return N;
+ };
- // Translates the django path to PHP and ASP.NET paths
- this.paths = function(str)
- {
- // For now, str will be the django path
- // This function will translate for other backends.
- if(server == "django")
- {
- return str;
- }
- };
+ // Calculates the X value and return it as a BigInteger
+ this.calcX = function(s)
+ {
+ return new BigInteger(SHA256(s + SHA256(I + ":" + p)), 16);
+ };
- // Get the text content of an XML node
- this.innerxml = function(node)
+ // Translates the django path to PHP and ASP.NET paths
+ this.paths = function(str)
+ {
+ // For now, str will be the django path
+ // This function will translate for other backends.
+ if(server == "django")
{
- return node.firstChild.nodeValue;
- };
+ return str;
+ }
+ };
- // Check whether or not a variable is defined
- function isdefined ( variable)
- {
- return (typeof(window[variable]) != "undefined");
- };
+ // Get the text content of an XML node
+ this.innerxml = function(node)
+ {
+ return node.firstChild.nodeValue;
+ };
+
+ // Check whether or not a variable is defined
+ function isdefined ( variable)
+ {
+ return (typeof(window[variable]) != "undefined");
+ };
- // *** Actions ***
+ // *** Actions ***
- // Perform ajax requests at the specified url, with the specified parameters
- // Calling back the specified function.
- this.ajaxRequest = function(full_url, params, callback)
+ // 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 = callback;
+ 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
{
- if( window.XMLHttpRequest)
- xhr = new XMLHttpRequest();
- else if (window.ActiveXObject){
- try{
- xhr = new ActiveXObject("Microsoft.XMLHTTP");
- }catch (e){}
+ that.error_message("Ajax failed.");
+ }
+ };
+
+ // Start the login process by identifying the user
+ this.identify = function()
+ {
+ var handshake_url = url + that.paths("handshake/");
+ var params = "I="+I+"&A="+Astr;
+ that.ajaxRequest(handshake_url, params, receive_salts);
+ };
+
+ // Receive login salts from the server, start calculations
+ function receive_salts()
+ {
+ if(xhr.readyState == 4 && xhr.status == 200) {
+ if(xhr.responseXML.getElementsByTagName("r").length > 0)
+ {
+ var response = xhr.responseXML.getElementsByTagName("r")[0];
+ // If there is no algorithm specified, calculate M given s, B, and P
+ if(!response.getAttribute("a"))
+ {
+ calculations(response.getAttribute("s"), response.getAttribute("B"), p);
+ that.ajaxRequest(url+that.paths("authenticate/"), "M="+M, confirm_authentication);
}
+ // If there is an algorithm specified, start the login process
else
+ upgrade(response.getAttribute("s"), response.getAttribute("B"), response.getAttribute("a"), response.getAttribute("d"));
+ }
+ else if(xhr.responseXML.getElementsByTagName("error").length > 0)
+ that.error_message(xhr.responseXML.getElementsByTagName("error")[0]);
+ }
+ };
+ // Calculate S, M, and M2
+ // This is the client side of the SRP specification
+ function calculations(s, ephemeral, pass)
+ {
+ //S -> C: s | B
+ var B = new BigInteger(ephemeral, 16);
+ var Bstr = ephemeral;
+ // u = H(A,B)
+ var u = new BigInteger(SHA256(Astr + Bstr), 16);
+ // x = H(s, H(I:p))
+ var x = new BigInteger(SHA256(s + SHA256(I + ":" + pass)), 16);
+ //S = (B - kg^x) ^ (a + ux)
+ var kgx = k.multiply(g.modPow(x, N));
+ var aux = a.add(u.multiply(x));
+ S = B.subtract(kgx).modPow(aux, N);
+ // M = H(H(N) xor H(g), H(I), s, A, B, K)
+ var Mstr = A.toString(16) + B.toString(16) + S.toString(16);
+ M = SHA256(Mstr);
+ M2 = SHA256(A.toString(16) + M + S.toString(16));
+ //M2 = H(A, M, K)
+ };
+
+ // Receive M2 from the server and verify it
+ function confirm_authentication()
+ {
+ if(xhr.readyState == 4 && xhr.status == 200) {
+ if(xhr.responseXML.getElementsByTagName("M").length > 0)
+ {
+ if(that.innerxml(xhr.responseXML.getElementsByTagName("M")[0]) == M2)
{
- that.error_message("Ajax not supported.");
- return;
- }
- if(xhr){
- xhr.onreadystatechange = callback;
- xhr.open("POST", full_url, true);
- xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
- xhr.setRequestHeader("Content-length", params.length);
- xhr.send(params);
+ authenticated = true;
+ success();
}
else
- {
- that.error_message("Ajax failed.");
- }
- };
+ that.error_message("Server key does not match");
+ }
+ else if (xhr.responseXML.getElementsByTagName("error").length > 0)
+ that.error_message(that.innerxml(xhr.responseXML.getElementsByTagName("error")[0]));
+ }
+ };
- // Start the login process by identifying the user
- this.identify = function()
- {
- var handshake_url = url + that.paths("handshake/");
- var params = "I="+I+"&A="+Astr;
- that.ajaxRequest(handshake_url, params, receive_salts);
- };
+ // *** Upgrades ***
- // Receive login salts from the server, start calculations
- function receive_salts()
- {
- if(xhr.readyState == 4 && xhr.status == 200) {
- if(xhr.responseXML.getElementsByTagName("r").length > 0)
- {
- var response = xhr.responseXML.getElementsByTagName("r")[0];
- // If there is no algorithm specified, calculate M given s, B, and P
- if(!response.getAttribute("a"))
- {
- calculations(response.getAttribute("s"), response.getAttribute("B"), p);
- that.ajaxRequest(url+that.paths("authenticate/"), "M="+M, confirm_authentication);
- }
- // If there is an algorithm specified, start the login process
- else
- upgrade(response.getAttribute("s"), response.getAttribute("B"), response.getAttribute("a"), response.getAttribute("d"));
- }
- else if(xhr.responseXML.getElementsByTagName("error").length > 0)
- that.error_message(xhr.responseXML.getElementsByTagName("error")[0]);
- }
- };
- // Calculate S, M, and M2
- // This is the client side of the SRP specification
- function calculations(s, ephemeral, pass)
- {
- //S -> C: s | B
- var B = new BigInteger(ephemeral, 16);
- var Bstr = ephemeral;
- // u = H(A,B)
- var u = new BigInteger(SHA256(Astr + Bstr), 16);
- // x = H(s, H(I:p))
- var x = new BigInteger(SHA256(s + SHA256(I + ":" + pass)), 16);
- //S = (B - kg^x) ^ (a + ux)
- var kgx = k.multiply(g.modPow(x, N));
- var aux = a.add(u.multiply(x));
- S = B.subtract(kgx).modPow(aux, N);
- // M = H(H(N) xor H(g), H(I), s, A, B, K)
- var Mstr = A.toString(16) + B.toString(16) + S.toString(16);
- M = SHA256(Mstr);
- M2 = SHA256(A.toString(16) + M + S.toString(16));
- //M2 = H(A, M, K)
- };
+ // Start the process to upgrade the user's account
+ function upgrade(s,ephemeral,algo,dsalt)
+ {
+ // First we need to import the hash functions
+ import_hashes();
- // Receive M2 from the server and verify it
- function confirm_authentication()
+ // Once the hash functions are imported, do the calculations using the hashpass as the password
+ function do_upgrade()
{
- if(xhr.readyState == 4 && xhr.status == 200) {
- if(xhr.responseXML.getElementsByTagName("M").length > 0)
- {
- if(that.innerxml(xhr.responseXML.getElementsByTagName("M")[0]) == M2)
- {
- authenticated = true;
- success();
- }
- else
- that.error_message("Server key does not match");
- }
- else if (xhr.responseXML.getElementsByTagName("error").length > 0)
- that.error_message(that.innerxml(xhr.responseXML.getElementsByTagName("error")[0]));
- }
+ // If sha1 and md5 are still undefined, sleep again
+ if(!isdefined("SHA1") || !isdefined("MD5"))
+ {
+ window.setTimeout(do_upgrade, 10);
+ return;
+ }
+ if(algo == "sha1")
+ hashfun = SHA1;
+ else if(algo == "md5")
+ hashfun = MD5;
+ //alert(hashfun(dsalt+p));
+ calculations(s, ephemeral, hashfun(dsalt+p));
+ that.ajaxRequest(url+that.paths("upgrade/authenticate/"), "M="+M, confirm_upgrade);
};
+ window.setTimeout(do_upgrade,10);
+ };
- // *** Upgrades ***
-
- // Start the process to upgrade the user's account
- function upgrade(s,ephemeral,algo,dsalt)
- {
- // First we need to import the hash functions
- import_hashes();
+ // Encrypt plaintext using slowAES
+ function encrypt(plaintext)
+ {
+ var key = cryptoHelpers.toNumbers(that.key());
+ var byteMessage = cryptoHelpers.convertStringToByteArray(plaintext);
+ var iv = new Array(16);
+ rng.nextBytes(iv);
+ var paddedByteMessage = slowAES.getPaddedBlock(byteMessage, 0, byteMessage.length, slowAES.modeOfOperation.CFB);
+ var ciphertext = slowAES.encrypt(paddedByteMessage, slowAES.modeOfOperation.CFB, key, key.length, iv).cipher;
+ var retstring = cryptoHelpers.base64.encode(iv.concat(ciphertext));
+ while(retstring.indexOf("+",0) > -1)
+ retstring = retstring.replace("+", "_");
+ return retstring;
+ };
- // Once the hash functions are imported, do the calculations using the hashpass as the password
- function do_upgrade()
+ // Receive the server's M, confirming that 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()
+ {
+ if(xhr.readyState == 4 && xhr.status == 200) {
+ if(xhr.responseXML.getElementsByTagName("M").length > 0)
+ {
+ if(that.innerxml(xhr.responseXML.getElementsByTagName("M")[0]) == M2)
{
- // If sha1 and md5 are still undefined, sleep again
- if(!isdefined("SHA1") || !isdefined("MD5"))
- {
- window.setTimeout(do_upgrade, 10);
- return;
- }
- if(algo == "sha1")
- hashfun = SHA1;
- else if(algo == "md5")
- hashfun = MD5;
- //alert(hashfun(dsalt+p));
- calculations(s, ephemeral, hashfun(dsalt+p));
- that.ajaxRequest(url+that.paths("upgrade/authenticate/"), "M="+M, confirm_upgrade);
- };
- window.setTimeout(do_upgrade,10);
- };
-
- // Encrypt plaintext using slowAES
- function encrypt(plaintext)
- {
- var key = cryptoHelpers.toNumbers(that.key());
- var byteMessage = cryptoHelpers.convertStringToByteArray(plaintext);
- var iv = new Array(16);
- rng.nextBytes(iv);
- var paddedByteMessage = slowAES.getPaddedBlock(byteMessage, 0, byteMessage.length, slowAES.modeOfOperation.CFB);
- var ciphertext = slowAES.encrypt(paddedByteMessage, slowAES.modeOfOperation.CFB, key, key.length, iv).cipher;
- var retstring = cryptoHelpers.base64.encode(iv.concat(ciphertext));
- while(retstring.indexOf("+",0) > -1)
- retstring = retstring.replace("+", "_");
- return retstring;
- };
-
- // Receive the server's M, confirming that 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()
- {
- if(xhr.readyState == 4 && xhr.status == 200) {
- if(xhr.responseXML.getElementsByTagName("M").length > 0)
- {
- if(that.innerxml(xhr.responseXML.getElementsByTagName("M")[0]) == M2)
- {
- K = SHA256(S.toString(16));
- var auth_url = url + that.paths("upgrade/verifier/");
- that.ajaxRequest(auth_url, "p="+encrypt(p)+"&l="+p.length, confirm_verifier);
- }
- else
- that.error_message("Server key does not match");
- }
- else if (xhr.responseXML.getElementsByTagName("error").length > 0)
- {
- that.error_message(that.innerxml(xhr.responseXML.getElementsByTagName("error")[0]));
- }
+ K = SHA256(S.toString(16));
+ var auth_url = url + that.paths("upgrade/verifier/");
+ that.ajaxRequest(auth_url, "p="+encrypt(p)+"&l="+p.length, confirm_verifier);
}
- };
-
- // After sending the password, check that the response is OK, then reidentify
- function confirm_verifier()
- {
- if(xhr.readyState == 4 && xhr.status == 200) {
- K = null;
- if(xhr.responseXML.getElementsByTagName("ok").length > 0)
- that.identify();
- else
- that.error_message("Verifier could not be confirmed");
- }
- };
-
- // This loads javascript libraries. Fname is the path to the library to be imported
- function import_file(fname)
- {
- var scriptElt = document.createElement('script');
- scriptElt.type = 'text/javascript';
- scriptElt.src = fname;
- document.getElementsByTagName('head')[0].appendChild(scriptElt);
- };
- // 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
- if(isdefined("SHA1") && isdefined("MD5")) return;
- // Get the directory that this javascript file was loaded from
- var arr=that.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")
- import_file(path+"/crypto.min.js");
- // Otherwise, this file is presumably srp.js, and we will load individual hash files
else
- {
- import_file(path+"/MD5.js");
- import_file(path+"/SHA1.js");
- import_file(path+"/cryptoHelpers.js");
- import_file(path+"/aes.js");
- }
+ that.error_message("Server key does not match");
+ }
+ else if (xhr.responseXML.getElementsByTagName("error").length > 0)
+ {
+ that.error_message(that.innerxml(xhr.responseXML.getElementsByTagName("error")[0]));
+ }
}
+ };
- function success()
+ // After sending the password, check that the response is OK, then reidentify
+ function confirm_verifier()
+ {
+ if(xhr.readyState == 4 && xhr.status == 200) {
+ K = null;
+ if(xhr.responseXML.getElementsByTagName("ok").length > 0)
+ that.identify();
+ else
+ that.error_message("Verifier could not be confirmed");
+ }
+ };
+
+ // This loads javascript libraries. Fname is the path to the library to be imported
+ function import_file(fname)
+ {
+ var scriptElt = document.createElement('script');
+ scriptElt.type = 'text/javascript';
+ scriptElt.src = fname;
+ document.getElementsByTagName('head')[0].appendChild(scriptElt);
+ };
+ // 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
+ if(isdefined("SHA1") && isdefined("MD5")) return;
+ // Get the directory that this javascript file was loaded from
+ var arr=that.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")
+ import_file(path+"/crypto.min.js");
+ // Otherwise, this file is presumably srp.js, and we will load individual hash files
+ else
{
- var forward_url = document.getElementById("srp_forward").value;
- if(forward_url.charAt(0) != "#")
- window.location = forward_url;
- else
- {
- window.location = forward_url;
- that.success();
- }
- };
- this.success = function()
+ import_file(path+"/MD5.js");
+ import_file(path+"/SHA1.js");
+ import_file(path+"/cryptoHelpers.js");
+ import_file(path+"/aes.js");
+ }
+ }
+
+ function success()
+ {
+ var forward_url = document.getElementById("srp_forward").value;
+ if(forward_url.charAt(0) != "#")
+ window.location = forward_url;
+ else
{
- alert("Login successful.");
- };
- // If someone wants to use the session key for encrypting traffic, they can
- // access the key with this function.
- this.key = function()
+ window.location = forward_url;
+ that.success();
+ }
+ };
+ this.success = function()
+ {
+ alert("Login successful.");
+ };
+ // If someone wants to use the session key for encrypting traffic, they can
+ // access the key with this function.
+ this.key = function()
+ {
+ if(K == null)
+ if(authenticated)
{
- if(K == null)
- if(authenticated)
- {
- K = SHA256(S.toString(16));
- return K;
- }
- else
- that.error_message("User has not been authenticated.");
- else
- return K;
- };
+ K = SHA256(S.toString(16));
+ return K;
+ }
+ else
+ that.error_message("User has not been authenticated.");
+ else
+ return K;
+ };
- // This function is called when authentication is successful.
- // Developers can set this to other functions in specific implementations
- // and change the functionality.
- /*this.success = function()
- {
- alert("Authentication successful.");
- };*/
+ // This function is called when authentication is successful.
+ // Developers can set this to other functions in specific implementations
+ // and change the functionality.
+ /*this.success = function()
+ {
+ alert("Authentication successful.");
+ };*/
// 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);
+ alert(t);
};
-};
-// 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.
-SRP.prototype.srpPath = document.getElementsByTagName('script')[document.getElementsByTagName('script').length-1].getAttribute("src");
+ };
+ // 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.
+ SRP.prototype.srpPath = document.getElementsByTagName('script')[document.getElementsByTagName('script').length-1].getAttribute("src");