diff -r dd2f2db1f87a netwerk/test/unit/test_authentication.js --- a/netwerk/test/unit/test_authentication.js Thu Mar 16 04:19:00 2017 +0100 +++ b/netwerk/test/unit/test_authentication.js Thu Mar 16 21:42:51 2017 +0600 @@ -18,16 +18,18 @@ XPCOMUtils.defineLazyGetter(this, "PORT" }); const FLAG_RETURN_FALSE = 1 << 0; const FLAG_WRONG_PASSWORD = 1 << 1; const FLAG_BOGUS_USER = 1 << 2; const FLAG_PREVIOUS_FAILED = 1 << 3; const CROSS_ORIGIN = 1 << 4; const FLAG_NO_REALM = 1 << 5; +const FLAG_UTF8_USER = 1 << 6; +const userUTF8 = "\u0443\u0442\u04448"; const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2; const nsIAuthInformation = Components.interfaces.nsIAuthInformation; function AuthPrompt1(flags) { this.flags = flags; } @@ -154,16 +156,18 @@ AuthPrompt2.prototype = { if (this.flags & FLAG_RETURN_FALSE) { this.flags |= FLAG_PREVIOUS_FAILED; return false; } if (this.flags & FLAG_BOGUS_USER) this.user = "foo\nbar"; + if (this.flags & FLAG_UTF8_USER) + this.user = userUTF8; authInfo.username = this.user; if (this.flags & FLAG_WRONG_PASSWORD) { authInfo.password = this.pass + ".wrong"; this.flags |= FLAG_PREVIOUS_FAILED; // Now clear the flag to avoid an infinite loop this.flags &= ~FLAG_WRONG_PASSWORD; } else { @@ -302,30 +306,31 @@ function makeChan(url, loadingUrl) { }); } var tests = [test_noauth, test_returnfalse1, test_wrongpw1, test_prompt1, test_prompt1CrossOrigin, test_prompt2CrossOrigin, test_returnfalse2, test_wrongpw2, test_prompt2, test_ntlm, test_basicrealm, test_digest_noauth, test_digest, test_digest_bogus_user, test_short_digest, test_large_realm, - test_large_domain]; + test_large_domain, test_userhash]; var current_test = 0; var httpserv = null; function run_test() { httpserv = new HttpServer(); httpserv.registerPathHandler("/auth", authHandler); httpserv.registerPathHandler("/auth/ntlm/simple", authNtlmSimple); httpserv.registerPathHandler("/auth/realm", authRealm); httpserv.registerPathHandler("/auth/digest", authDigest); httpserv.registerPathHandler("/auth/short_digest", authShortDigest); + httpserv.registerPathHandler("/auth/digest/userhash", authUserhash); httpserv.registerPathHandler("/largeRealm", largeRealm); httpserv.registerPathHandler("/largeDomain", largeDomain); httpserv.start(-1); tests[0](); } @@ -446,17 +451,17 @@ function test_digest_noauth() { chan.asyncOpen2(listener); do_test_pending(); } function test_digest() { var chan = makeChan(URL + "/auth/digest", URL); - chan.notificationCallbacks = new Requestor(0, 2); + chan.notificationCallbacks = new Requestor(FLAG_UTF8_USER, 2); listener.expectedCode = 200; // OK chan.asyncOpen2(listener); do_test_pending(); } function test_digest_bogus_user() { var chan = makeChan(URL + "/auth/digest", URL); @@ -472,16 +477,26 @@ function test_short_digest() { var chan = makeChan(URL + "/auth/short_digest", URL); chan.notificationCallbacks = new Requestor(FLAG_NO_REALM, 2); listener.expectedCode = 401; // OK chan.asyncOpen2(listener); do_test_pending(); } +function test_userhash() { + var chan = makeChan(URL + "/auth/digest/userhash", URL); + + chan.notificationCallbacks = new Requestor(0, 2); + listener.expectedCode = 200; // OK + chan.asyncOpen2(listener); + + do_test_pending(); +} + // PATH HANDLERS // /auth function authHandler(metadata, response) { // btoa("guest:guest"), but that function is not available here var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q="; var body; @@ -539,57 +554,65 @@ function bytesFromString(str) { return data; } // return the two-digit hexadecimal code for a byte function toHexString(charCode) { return ("0" + charCode.toString(16)).slice(-2); } -function H(str) { +function H(str, algorithm) { var data = bytesFromString(str); var ch = Components.classes["@mozilla.org/security/hash;1"] .createInstance(Components.interfaces.nsICryptoHash); - ch.init(Components.interfaces.nsICryptoHash.MD5); + if (algorithm == "MD5") { + ch.init(Components.interfaces.nsICryptoHash.MD5); + } else if (algorithm == "SHA-256") { + ch.init(Components.interfaces.nsICryptoHash.SHA256); + } else { + do_throw("Algorithm not set!"); + } ch.update(data, data.length); var hash = ch.finish(false); return Array.from(hash, (c, i) => toHexString(hash.charCodeAt(i))).join(""); } // // Digest handler // // /auth/digest function authDigest(metadata, response) { var nonce = "6f93719059cf8d568005727f3250e798"; var opaque = "1234opaque1234"; var cnonceRE = /cnonce="(\w+)"/; var responseRE = /response="(\w+)"/; - var usernameRE = /username="(\w+)"/; + var usernameRE = /username(?=="\w+"|\*=UTF-8'')(?:="|\*=UTF-8'')([%\w]+)/; + var algorithmRE = /algorithm=(\w+[-\d]*)/; var authenticate = 'Digest realm="secret", domain="/", qop=auth,' + - 'algorithm=MD5, nonce="' + nonce+ '" opaque="' + - opaque + '"'; + 'algorithm=SHA-256, nonce="' + nonce+ '", opaque="' + + opaque + '", charset=UTF-8'; var body; // check creds if we have them if (metadata.hasHeader("Authorization")) { var auth = metadata.getHeader("Authorization"); var cnonce = (auth.match(cnonceRE))[1]; var clientDigest = (auth.match(responseRE))[1]; - var username = (auth.match(usernameRE))[1]; + var username = decodeURI((auth.match(usernameRE))[1]); + var algorithm = (auth.match(algorithmRE))[1]; var nc = "00000001"; - if (username != "guest") { + if (username != userUTF8) { response.setStatusLine(metadata.httpVersion, 400, "bad request"); body = "should never get here"; } else { // see RFC2617 for the description of this calculation - var A1 = "guest:secret:guest"; + var A1 = userUTF8 + ":secret:guest"; var A2 = "GET:/auth/digest"; - var noncebits = [nonce, nc, cnonce, "auth", H(A2)].join(":"); - var digest = H([H(A1), noncebits].join(":")); + var noncebits = [nonce, nc, cnonce, "auth", H(A2, algorithm)].join(":"); + var digest = H([H(A1, algorithm), noncebits].join(":"), algorithm); if (clientDigest == digest) { response.setStatusLine(metadata.httpVersion, 200, "OK, authorized"); body = "success"; } else { response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); response.setHeader("WWW-Authenticate", authenticate, false); body = "auth failed"; @@ -2073,16 +2096,47 @@ function largeDomain(metadata, response) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + '"'); body = "need to authenticate"; response.bodyOutputStream.write(body, body.length); } +function authUserhash(metadata, response) { + var usernameRE = /username="(\w+)"/; + var algorithmRE = /algorithm=(\w+[-\d]*)/; + var userhashRE = /userhash=(\w+)/; + var authenticate = 'Digest realm="secret", algorithm=SHA-256, userhash=true'; + var body; + if (metadata.hasHeader("Authorization")) { + var auth = metadata.getHeader("Authorization"); + var username = (auth.match(usernameRE))[1]; + var algorithm = (auth.match(algorithmRE))[1]; + var userhash = (auth.match(userhashRE))[1]; + if (userhash == "true") { + if (username != H("guest:secret", algorithm)) { + response.setStatusLine(metadata.httpVersion, 400, "bad request"); + body = "should never get here"; + } else { + response.setStatusLine(metadata.httpVersion, 200, "OK, authorized"); + body = "success"; + } + } else { + response.setStatusLine(metadata.httpVersion, 400, "bad request"); + body = "need userhash"; + } + } else { + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", authenticate, false); + var body = "failed, no header"; + } + response.bodyOutputStream.write(body, body.length); +} + function test_large_realm() { var chan = makeChan(URL + "/largeRealm", URL); listener.expectedCode = 401; // Unauthorized chan.asyncOpen2(listener); do_test_pending(); }