diff options
Diffstat (limited to 'javascript')
-rw-r--r-- | javascript/srp.js | 626 | ||||
-rw-r--r-- | javascript/srp_register.js | 90 |
2 files changed, 358 insertions, 358 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"); diff --git a/javascript/srp_register.js b/javascript/srp_register.js index 31b8ab8..670e59d 100644 --- a/javascript/srp_register.js +++ b/javascript/srp_register.js @@ -1,52 +1,52 @@ function SRP_REGISTER() { - var that; + var that; - // 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); - }; + // 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); + }; - // Receive the salt for registration - SRP.prototype.register_receive_salt = function() - { - var xhr = that.getxhr(); - if(xhr.readyState == 4 && xhr.status == 200) { - if(xhr.responseXML.getElementsByTagName("salt").length > 0) - { - var s = that.innerxml(xhr.responseXML.getElementsByTagName("salt")[0]); - var x = that.calcX(s); - var v = that.getg().modPow(x, that.getN()); - that.register_send_verifier(v.toString(16)); - } - else if(xhr.responseXML.getElementsByTagName("error").length > 0) - { - that.error_message(that.innerxml(xhr.responseXML.getElementsByTagName("error")[0])); - } - } - }; - // 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.register_user); - }; + // Receive the salt for registration + SRP.prototype.register_receive_salt = function() + { + var xhr = that.getxhr(); + if(xhr.readyState == 4 && xhr.status == 200) { + if(xhr.responseXML.getElementsByTagName("salt").length > 0) + { + var s = that.innerxml(xhr.responseXML.getElementsByTagName("salt")[0]); + var x = that.calcX(s); + var v = that.getg().modPow(x, that.getN()); + that.register_send_verifier(v.toString(16)); + } + else if(xhr.responseXML.getElementsByTagName("error").length > 0) + { + that.error_message(that.innerxml(xhr.responseXML.getElementsByTagName("error")[0])); + } + } + }; + // 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.register_user); + }; - // The user has been registered successfully, now login - SRP.prototype.register_user = function() - { - var xhr = that.getxhr(); - if(xhr.readyState == 4 && xhr.status == 200) { - if(xhr.responseXML.getElementsByTagName("ok").length > 0) - { - that.identify(); - } - } - }; + // The user has been registered successfully, now login + SRP.prototype.register_user = function() + { + var xhr = that.getxhr(); + if(xhr.readyState == 4 && xhr.status == 200) { + if(xhr.responseXML.getElementsByTagName("ok").length > 0) + { + that.identify(); + } + } + }; }; SRP_REGISTER(); |