javascript - Using ntlm authentication in Nativescript ios platform -
i building app authenticates user against sharepoint site uses ntlm authentication. found ntlm.js has been patched nativescript here https://github.com/hdeshev/nativescript-ntlm-demo.
i have managed working android platform, fails on ios showing 401 error. far can tell, difference happens in segment:
ntlm.setcredentials = function(domain, username, password) { var magic = 'kgs!@#$%'; // create lm password hash. var lmpassword = password.touppercase().substr(0, 14); while (lmpassword.length < 14) lmpassword += '\0'; var key1 = ntlm.createkey(lmpassword); var key2 = ntlm.createkey(lmpassword.substr(7)); var lmhashedpassword = des(key1, magic, 1, 0) + des(key2, magic, 1, 0); var ntpassword = ''; // create nt password hash. (var = 0; < password.length; i++) ntpassword += password.charat(i) + '\0'; var nthashedpassword = str_md4(ntpassword); ntlm.domain = domain; ntlm.username = username; ntlm.lmhashedpassword = lmhashedpassword; ntlm.nthashedpassword = nthashedpassword; };
when log result of 'lmhashedpassword' after going through des()
function, returns 'a'. whereas on android, returns longer string. in des
function must cutting off, cannot see what.
here des
function:
function des (key, message, encrypt, mode, iv, padding) { //declaring locally speeds things bit var spfunction1 = new array (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004); var spfunction2 = new array (-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000); var spfunction3 = new array (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200); var spfunction4 = new array (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080); var spfunction5 = new array (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100); var spfunction6 = new array (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010); var spfunction7 = new array (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002); var spfunction8 = new array (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000); //create 16 or 48 subkeys need var keys = des_createkeys (key); var m=0, i, j, temp, temp2, right1, right2, left, right, looping; var cbcleft, cbcleft2, cbcright, cbcright2 var endloop, loopinc; var len = message.length; var chunk = 0; //set loops single , triple des var iterations = keys.length == 32 ? 3 : 9; //single or triple des if (iterations == 3) {looping = encrypt ? new array (0, 32, 2) : new array (30, -2, -2);} else {looping = encrypt ? new array (0, 32, 2, 62, 30, -2, 64, 96, 2) : new array (94, 62, -2, 32, 64, 2, 30, -2, -2);} //pad message depending on padding parameter if (padding == 2) message += " "; //pad message spaces else if (padding == 1) {temp = 8-(len%8); message += string.fromcharcode (temp,temp,temp,temp,temp,temp,temp,temp); if (temp==8) len+=8;} //pkcs7 padding else if (!padding) message += "\0\0\0\0\0\0\0\0"; //pad message out null bytes //store result here result = ""; tempresult = ""; if (mode == 1) { //cbc mode cbcleft = (iv.charcodeat(m++) << 24) | (iv.charcodeat(m++) << 16) | (iv.charcodeat(m++) << 8) | iv.charcodeat(m++); cbcright = (iv.charcodeat(m++) << 24) | (iv.charcodeat(m++) << 16) | (iv.charcodeat(m++) << 8) | iv.charcodeat(m++); m=0; } //loop through each 64 bit chunk of message while (m < len) { left = (message.charcodeat(m++) << 24) | (message.charcodeat(m++) << 16) | (message.charcodeat(m++) << 8) | message.charcodeat(m++); right = (message.charcodeat(m++) << 24) | (message.charcodeat(m++) << 16) | (message.charcodeat(m++) << 8) | message.charcodeat(m++); //for cipher block chaining mode, xor message previous result if (mode == 1) {if (encrypt) {left ^= cbcleft; right ^= cbcright;} else {cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right;}} //first each 64 chunk of message must permuted according ip temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); left = ((left << 1) | (left >>> 31)); right = ((right << 1) | (right >>> 31)); //do either 1 or 3 times each chunk of message (j=0; j<iterations; j+=3) { endloop = looping[j+1]; loopinc = looping[j+2]; //now go through , perform encryption or decryption (i=looping[j]; i!=endloop; i+=loopinc) { //for efficiency right1 = right ^ keys[i]; right2 = ((right >>> 4) | (right << 28)) ^ keys[i+1]; //the result attained passing these bytes through s selection functions temp = left; left = right; right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f] | spfunction6[(right1 >>> 8) & 0x3f] | spfunction8[right1 & 0x3f] | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f] | spfunction5[(right2 >>> 8) & 0x3f] | spfunction7[right2 & 0x3f]); } temp = left; left = right; right = temp; //unreverse left , right } //for either 1 or 3 iterations //move each 1 bit right left = ((left >>> 1) | (left << 31)); right = ((right >>> 1) | (right << 31)); //now perform ip-1, ip in opposite direction temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); //for cipher block chaining mode, xor message previous result if (mode == 1) {if (encrypt) {cbcleft = left; cbcright = right;} else {left ^= cbcleft2; right ^= cbcright2;}} tempresult += string.fromcharcode ((left>>>24), ((left>>>16) & 0xff), ((left>>>8) & 0xff), (left & 0xff), (right>>>24), ((right>>>16) & 0xff), ((right>>>8) & 0xff), (right & 0xff)); chunk += 8; if (chunk == 512) {result += tempresult; tempresult = ""; chunk = 0;} } //for every 8 characters, or 64 bits in message //return result array return result + tempresult; } //end of des
in case may relevant, have changed way request made too. when user clicks login, following promise called:
ntlm.login('url') .then(() => { console.log('success'); appsettings.setstring('token', 'abc123'); this.router.navigate(['/ilt']); }) .catch(error => { console.log('failed'); appsettings.remove('token'); alert('failed! ' + error ); })
i created new login function in ntlm.js file:
ntlm.login = function(url) { return new promise((resolve, reject) => { if (!ntlm.domain || !ntlm.username || !ntlm.lmhashedpassword || !ntlm.nthashedpassword) { ntlm.error('no ntlm credentials specified. use ntlm.setcredentials(...) before making calls.'); } var hostname = ntlm.getlocation(url).hostname; var msg1 = ntlm.createmessage1(hostname); var request = new xmlhttprequest(); request.onload = function() { var response = request.getresponseheader('www-authenticate'); var challenge = ntlm.getchallenge(response); var msg3 = ntlm.createmessage3(challenge, hostname); request.open('get', url, false); var authorization = 'ntlm ' + msg3.tobase64(); request.setrequestheader('authorization', authorization); request.onload = function() { if (request.readystate == 4 && request.status == 200) { resolve(request.status); } else if (request.readystate == 4 && request.status != 200) { reject(request.status); } }; request.send(null); }; request.open('get', url, false); request.setrequestheader('authorization', 'ntlm ' + msg1.tobase64()); request.send(null); }) };
this working fine on android version, cant understand why isnt on ios. frustrating! if can make sense of this, eternally grateful. realise lot of code , quite niche area!
many thanks,
update
i think there may difference in way console.log behaves in android , ios, explain of missing characters. created new test account (testuser / testing), , logged various points try , establish happening in ntlm process step step. here logs android:
ntlm walkthrough on android step 1: creates cryptographic hash of users password: lmhashedpassword = -ue}{}*ªÓ´5µî nthashedpassword = |sÏ¥ê}�;�� ûq£õ step 2: sends first request server, following authorisation header: ntlm tlrmtvntuaabaaaaa7iaaauabqbeaaaajaakacaaaabhqvrfv0fzllnuuefvtfndqvrit0xjq0nptexfr0uuq08uvutbre1jtg== step 3: server sends challenge client: ¡2@�³q%Ï step 4: client encrypts challenge hash of users password , sends server (response). authorization header is: ntlm tlrmtvntuaadaaaagaayakqaaaayabgavaaaaaoacgbaaaaaegasaeoaaabiaegaxaaaaaaaaaduaaaaayiaaeearabnaekatgb0aguacwb0ahmadabhagyazgbhaeeavabfafcaqqbzac4auwbuafaaqqbvaewauwbdaeeavabiae8atabjaemaqwbpaewatabfaecarqauaematwauafuaswbseslcvtqhhy3+rgktqufbzfrmuffknkahxjrca6thoau105+njbgnsn2ri6ziuv8= step 5: server has sent username, challenge , response domain controller. dc compares , returns status of: 200
here logs ios:
ntlm walkthrough on ios step 1: creates cryptographic hash of users password: lmhashedpassword = -ue}{}*ªÓ´5µî nthashedpassword = |sÏ¥ê}�;�� ûq£õ step 2: sends first request server, following authorisation header: ntlm tlrmtvntuaabaaaaa7iaaauabqbeaaaajaakacaaaabhqvrfv0fzllnuuefvtfndqvrit0xjq0nptexfr0uuq08uvutbre1jtg== step 3: server sends challenge client: q�v¹, step 4: client encrypts challenge hash of users password , sends server (response). authorization header is: ntlm tlrmtvntuaadaaaagaayakqaaaayabgavaaaaaoacgbaaaaaegasaeoaaabiaegaxaaaaaaaaaduaaaaayiaaeearabnaekatgb0aguacwb0ahmadabhagyazgbhaeeavabfafcaqqbzac4auwbuafaaqqbvaewauwbdaeeavabiae8atabjaemaqwbpaewatabfaecarqauaematwauafuaswap9hn5wjpcs9hmrrmttnyhiefrthwyuawankwtvdzoqdoj2isudqev0ismv9tt0ek= step 5: server has sent username, challenge , response domain controller. dc compares returns status of: 401
seems credentials worked out same, , challenge returned server random. on ios, challenge seems missing characters - possibly due type of characters. client encrypts challenge hashed passwords , sends server. imagine might part not correct on ios.
Comments
Post a Comment