UNPKG

phpjs

Version:

php.js offers community built php functions in javascript

877 lines (724 loc) 37.8 kB
<!-- Generated by Rakefile:build --> <strong> <a href="ERASEthemasterswordsman.com" rel="nofollow">ERASEthemasterswordsman</a> </strong> on 2012-06-07 13:11:02 <br /> Please note that hashing a has is not completely secure, but can be used as an extra precaution (but not recommended): <pre><code> &lt;?php sha1(md5($string)); ?&gt; </code></pre> <hr /> <strong> <a href="http://g.miernicki.com" rel="nofollow">G</a> </strong> on 2009-01-17 00:26:39 <br /> fuckin wicked code. nice, neat, small, clean, and very useful :) <hr /> <strong> <a href="http://kevin.vanzonneveld.net" rel="nofollow">Kevin van Zonneveld</a> </strong> on 2008-11-25 17:10:59 <br /> @ Patrick Schlicher: Thanks for the info! <hr /> <strong> Onno Marsman </strong> on 2008-11-19 19:22:27 <br /> Mainda: Unfortunately I don't know anything about any sha1 algorithm. I just posted some code I found somewhere in the past. I've been using this code successfully for a long time, also in IE7. So I don't know where your error could have come from. Looking at the code from this library: I've been wondering in the past about the following line of code: <pre><code> str = utf8_encode(str); </code></pre> I know this is not part of the sha1 algorithm and is probably necessary because of the way browsers handle character encoding, although I don't really understand it. You could try removing this line. Let me know whether it worked. <hr /> <strong> Mainda Siriwardana </strong> on 2008-11-19 14:42:18 <br /> hi! Onno Marsman, I would like to know whether its working on IE(7) and if possible let me know more details about it. sha1 originally mentionaed is not working properly for binary file. this code is working fine on FF but not on IE saying &amp;quot;this method is not supported&amp;quot; thanks! <hr /> <strong> <a href="http://www.n8reise.de" rel="nofollow">Patrick Schlicher</a> </strong> on 2008-11-19 09:07:58 <br /> I was searching for an encryption like blowfish that works with PHP and JS for long time. Finally I found a working AES implementation: JS: http://www.movable-type.co.uk/scripts/aes.html PHP: http://www.movable-type.co.uk/scripts/aes-php.html <pre><code>/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* AES implementation in JavaScript (c) Chris Veness 2005-2008 */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* * AES Cipher function: encrypt 'input' with Rijndael algorithm * * takes byte-array 'input' (16 bytes) * 2D byte-array key schedule 'w' (Nr+1 x Nb bytes) * * applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage * * returns byte-array encrypted value (16 bytes) */ function Cipher(input, w) { // main Cipher function [§5.1] var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4] for (var i=0; i&amp;lt;4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i]; state = AddRoundKey(state, w, 0, Nb); for (var round=1; round&amp;lt;Nr; round++) { state = SubBytes(state, Nb); state = ShiftRows(state, Nb); state = MixColumns(state, Nb); state = AddRoundKey(state, w, round, Nb); } state = SubBytes(state, Nb); state = ShiftRows(state, Nb); state = AddRoundKey(state, w, Nr, Nb); var output = new Array(4*Nb); // convert state to 1-d array before returning [§3.4] for (var i=0; i&amp;lt;4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)]; return output; } function SubBytes(s, Nb) { // apply SBox to state S [§5.1.1] for (var r=0; r&amp;lt;4; r++) { for (var c=0; c&amp;lt;Nb; c++) s[r][c] = Sbox[s[r][c]]; } return s; } function ShiftRows(s, Nb) { // shift row r of state S left by r bytes [§5.1.2] var t = new Array(4); for (var r=1; r&amp;lt;4; r++) { for (var c=0; c&amp;lt;4; c++) t[c] = s[r][(c+r)%Nb]; // shift into temp copy for (var c=0; c&amp;lt;4; c++) s[r][c] = t[c]; // and copy back } // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES): return s; // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf } function MixColumns(s, Nb) { // combine bytes of each col of state S [§5.1.3] for (var c=0; c&amp;lt;4; c++) { var a = new Array(4); // 'a' is a copy of the current column from 's' var b = new Array(4); // 'b' is a•{02} in GF(2^8) for (var i=0; i&amp;lt;4; i++) { a[i] = s[i][c]; b[i] = s[i][c]&amp;amp;0x80 ? s[i][c]&amp;lt;&amp;lt;1 ^ 0x011b : s[i][c]&amp;lt;&amp;lt;1; } // a[n] ^ b[n] is a•{03} in GF(2^8) s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // 2*a0 + 3*a1 + a2 + a3 s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 * 2*a1 + 3*a2 + a3 s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + 2*a2 + 3*a3 s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // 3*a0 + a1 + a2 + 2*a3 } return s; } function AddRoundKey(state, w, rnd, Nb) { // xor Round Key into state S [§5.1.4] for (var r=0; r&amp;lt;4; r++) { for (var c=0; c&amp;lt;Nb; c++) state[r][c] ^= w[rnd*4+c][r]; } return state; } function KeyExpansion(key) { // generate Key Schedule (byte-array Nr+1 x Nb) from Key [§5.2] var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) var Nk = key.length/4 // key length (in words): 4/6/8 for 128/192/256-bit keys var Nr = Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys var w = new Array(Nb*(Nr+1)); var temp = new Array(4); for (var i=0; i&amp;lt;Nk; i++) { var r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]]; w[i] = r; } for (var i=Nk; i&amp;lt;(Nb*(Nr+1)); i++) { w[i] = new Array(4); for (var t=0; t&amp;lt;4; t++) temp[t] = w[i-1][t]; if (i % Nk == 0) { temp = SubWord(RotWord(temp)); for (var t=0; t&amp;lt;4; t++) temp[t] ^= Rcon[i/Nk][t]; } else if (Nk &amp;gt; 6 &amp;amp;&amp;amp; i%Nk == 4) { temp = SubWord(temp); } for (var t=0; t&amp;lt;4; t++) w[i][t] = w[i-Nk][t] ^ temp[t]; } return w; } function SubWord(w) { // apply SBox to 4-byte word w for (var i=0; i&amp;lt;4; i++) w[i] = Sbox[w[i]]; return w; } function RotWord(w) { // rotate 4-byte word w left by one byte var tmp = w[0]; for (var i=0; i&amp;lt;3; i++) w[i] = w[i+1]; w[3] = tmp; return w; } // Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1] var Sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]; // Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2] var Rcon = [ [0x00, 0x00, 0x00, 0x00], [0x01, 0x00, 0x00, 0x00], [0x02, 0x00, 0x00, 0x00], [0x04, 0x00, 0x00, 0x00], [0x08, 0x00, 0x00, 0x00], [0x10, 0x00, 0x00, 0x00], [0x20, 0x00, 0x00, 0x00], [0x40, 0x00, 0x00, 0x00], [0x80, 0x00, 0x00, 0x00], [0x1b, 0x00, 0x00, 0x00], [0x36, 0x00, 0x00, 0x00] ]; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Encrypt a text using AES encryption in Counter mode of operation * - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf * * Unicode multi-byte character safe * * @param plaintext source text to be encrypted * @param password the password to use to generate a key * @param nBits number of bits to be used in the key (128, 192, or 256) * @return encrypted text */ function AESEncryptCtr(plaintext, password, nBits) { var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys plaintext = plaintext.encodeUTF8(); password = password.encodeUTF8(); //var t = new Date(); // timer // use AES itself to encrypt password to get cipher key (using plain password as source for key // expansion) - gives us well encrypted key var nBytes = nBits/8; // no bytes in key var pwBytes = new Array(nBytes); for (var i=0; i&amp;lt;nBytes; i++) { pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i); } var key = Cipher(pwBytes, KeyExpansion(pwBytes)); // gives us 16-byte key key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long // initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in 1st 8 bytes, // block counter in 2nd 8 bytes var counterBlock = new Array(blockSize); var nonce = (new Date()).getTime(); // timestamp: milliseconds since 1-Jan-1970 var nonceSec = Math.floor(nonce/1000); var nonceMs = nonce%1000; // encode nonce with seconds in 1st 4 bytes, and (repeated) ms part filling 2nd 4 bytes for (var i=0; i&amp;lt;4; i++) counterBlock[i] = (nonceSec &amp;gt;&amp;gt;&amp;gt; i*8) &amp;amp; 0xff; for (var i=0; i&amp;lt;4; i++) counterBlock[i+4] = nonceMs &amp;amp; 0xff; // and convert it to a string to go on the front of the ciphertext var ctrTxt = ''; for (var i=0; i&amp;lt;8; i++) ctrTxt += String.fromCharCode(counterBlock[i]); // generate key schedule - an expansion of the key into distinct Key Rounds for each round var keySchedule = KeyExpansion(key); var blockCount = Math.ceil(plaintext.length/blockSize); var ciphertxt = new Array(blockCount); // ciphertext as array of strings for (var b=0; b&amp;lt;blockCount; b++) { // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) // done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB) for (var c=0; c&amp;lt;4; c++) counterBlock[15-c] = (b &amp;gt;&amp;gt;&amp;gt; c*8) &amp;amp; 0xff; for (var c=0; c&amp;lt;4; c++) counterBlock[15-c-4] = (b/0x100000000 &amp;gt;&amp;gt;&amp;gt; c*8) var cipherCntr = Cipher(counterBlock, keySchedule); // -- encrypt counter block -- // block size is reduced on final block var blockLength = b&amp;lt;blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1; var cipherChar = new Array(blockLength); for (var i=0; i&amp;lt;blockLength; i++) { // -- xor plaintext with ciphered counter char-by-char -- cipherChar[i] = cipherCntr[i] ^ plaintext.charCodeAt(b*blockSize+i); cipherChar[i] = String.fromCharCode(cipherChar[i]); } ciphertxt[b] = cipherChar.join(''); } // Array.join is more efficient than repeated string concatenation var ciphertext = ctrTxt + ciphertxt.join(''); ciphertext = ciphertext.encodeBase64(); // encode in base64 //alert((new Date()) - t); return ciphertext; } /** * Decrypt a text encrypted by AES in counter mode of operation * * @param ciphertext source text to be encrypted * @param password the password to use to generate a key * @param nBits number of bits to be used in the key (128, 192, or 256) * @return decrypted text */ function AESDecryptCtr(ciphertext, password, nBits) { var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys ciphertext = ciphertext.decodeBase64(); password = password.encodeUTF8(); //var t = new Date(); // timer // use AES to encrypt password (mirroring encrypt routine) var nBytes = nBits/8; // no bytes in key var pwBytes = new Array(nBytes); for (var i=0; i&amp;lt;nBytes; i++) { pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i); } var key = Cipher(pwBytes, KeyExpansion(pwBytes)); key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long // recover nonce from 1st 8 bytes of ciphertext var counterBlock = new Array(8); ctrTxt = ciphertext.slice(0, 8); for (var i=0; i&amp;lt;8; i++) counterBlock[i] = ctrTxt.charCodeAt(i); // generate key schedule var keySchedule = KeyExpansion(key); // separate ciphertext into blocks (skipping past initial 8 bytes) var nBlocks = Math.ceil((ciphertext.length-8) / blockSize); var ct = new Array(nBlocks); for (var b=0; b&amp;lt;nBlocks; b++) ct[b] = ciphertext.slice(8+b*blockSize, 8+b*blockSize+blockSize); ciphertext = ct; // ciphertext is now array of block-length strings // plaintext will get generated block-by-block into array of block-length strings var plaintxt = new Array(ciphertext.length); for (var b=0; b&amp;lt;nBlocks; b++) { // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) for (var c=0; c&amp;lt;4; c++) counterBlock[15-c] = ((b) &amp;gt;&amp;gt;&amp;gt; c*8) &amp;amp; 0xff; for (var c=0; c&amp;lt;4; c++) counterBlock[15-c-4] = (((b+1)/0x100000000-1) &amp;gt;&amp;gt;&amp;gt; c*8) &amp;amp; 0xff; var cipherCntr = Cipher(counterBlock, keySchedule); // encrypt counter block var plaintxtByte = new Array(ciphertext[b].length); for (var i=0; i&amp;lt;ciphertext[b].length; i++) { // -- xor plaintxt with ciphered counter byte-by-byte -- plaintxtByte[i] = cipherCntr[i] ^ ciphertext[b].charCodeAt(i); plaintxtByte[i] = String.fromCharCode(plaintxtByte[i]); } plaintxt[b] = plaintxtByte.join(''); } // join array of blocks into single plaintext string var plaintext = plaintxt.join(''); plaintext = plaintext.decodeUTF8(); // decode from UTF8 back to Unicode multi-byte chars //alert((new Date()) - t); return plaintext; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Encode string into Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648] * (instance method extending String object). As per RFC 4648, no newlines are added. * * @param utf8encode optional parameter, if set to true Unicode string is encoded to UTF8 before * conversion to base64; otherwise string is assumed to be 8-bit characters * @return base64-encoded string */ var b64 = &amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=&amp;quot;; String.prototype.encodeBase64 = function(utf8encode) { // http://tools.ietf.org/html/rfc4648 utf8encode = (typeof utf8encode == 'undefined') ? false : utf8encode; var o1, o2, o3, bits, h1, h2, h3, h4, e=[], pad = '', c, plain, coded; plain = utf8encode ? this.encodeUTF8() : this; c = plain.length % 3; // pad string to length of multiple of 3 if (c &amp;gt; 0) { while (c++ &amp;lt; 3) { pad += '='; plain += '\0'; } } // note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars for (c=0; c&amp;lt;plain.length; c+=3) { // pack three octets into four hexets o1 = plain.charCodeAt(c); o2 = plain.charCodeAt(c+1); o3 = plain.charCodeAt(c+2); bits = o1&amp;lt;&amp;lt;16 | o2&amp;lt;&amp;lt;8 | o3; h1 = bits&amp;gt;&amp;gt;18 &amp;amp; 0x3f; h2 = bits&amp;gt;&amp;gt;12 &amp;amp; 0x3f; h3 = bits&amp;gt;&amp;gt;6 &amp;amp; 0x3f; h4 = bits &amp;amp; 0x3f; // use hextets to index into b64 string e[c/3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } coded = e.join(''); // join() is far faster than repeated string concatenation // replace 'A's from padded nulls with '='s coded = coded.slice(0, coded.length-pad.length) + pad; return coded; } /** * Decode string from Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648] * (instance method extending String object). As per RFC 4648, newlines are not catered for. * * @param utf8decode optional parameter, if set to true UTF8 string is decoded back to Unicode * after conversion from base64 * @return decoded string */ String.prototype.decodeBase64 = function(utf8decode) { utf8decode = (typeof utf8decode == 'undefined') ? false : utf8decode; var o1, o2, o3, h1, h2, h3, h4, bits, d=[], plain, coded; coded = utf8decode ? this.decodeUTF8() : this; for (var c=0; c&amp;lt;coded.length; c+=4) { // unpack four hexets into three octets h1 = b64.indexOf(coded.charAt(c)); h2 = b64.indexOf(coded.charAt(c+1)); h3 = b64.indexOf(coded.charAt(c+2)); h4 = b64.indexOf(coded.charAt(c+3)); bits = h1&amp;lt;&amp;lt;18 | h2&amp;lt;&amp;lt;12 | h3&amp;lt;&amp;lt;6 | h4; o1 = bits&amp;gt;&amp;gt;&amp;gt;16 &amp;amp; 0xff; o2 = bits&amp;gt;&amp;gt;&amp;gt;8 &amp;amp; 0xff; o3 = bits &amp;amp; 0xff; d[c/4] = String.fromCharCode(o1, o2, o3); // check for padding if (h4 == 0x40) d[c/4] = String.fromCharCode(o1, o2); if (h3 == 0x40) d[c/4] = String.fromCharCode(o1); } plain = d.join(''); // join() is far faster than repeated string concatenation return utf8decode ? plain.decodeUTF8() : plain; } /** * Encode multi-byte Unicode string into utf-8 multiple single-byte characters * (BMP / basic multilingual plane only) (instance method extending String object). * * Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars * * @return encoded string */ String.prototype.encodeUTF8 = function() { // use regular expressions &amp;amp; String.replace callback function for better efficiency // than procedural approaches var str = this.replace( /[\u0080-\u07ff]/g, // U+0080 - U+07FF =&amp;gt; 2 bytes 110yyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xc0 | cc&amp;gt;&amp;gt;6, 0x80 | cc&amp;amp;0x3f); } ); str = str.replace( /[\u0800-\uffff]/g, // U+0800 - U+FFFF =&amp;gt; 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xe0 | cc&amp;gt;&amp;gt;12, 0x80 | cc&amp;gt;&amp;gt;6&amp;amp;0x3F, 0x80 | cc&amp;amp;0x3f); } ); return str; } /** * Decode utf-8 encoded string back into multi-byte Unicode characters * (instance method extending String object). * * @return decoded string */ String.prototype.decodeUTF8 = function() { var str = this.replace( /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars function(c) { // (note parentheses for precence) var cc = (c.charCodeAt(0)&amp;amp;0x1f)&amp;lt;&amp;lt;6 | c.charCodeAt(1)&amp;amp;0x3f; return String.fromCharCode(cc); } ); str = str.replace( /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars function(c) { // (note parentheses for precence) var cc = ((c.charCodeAt(0)&amp;amp;0x0f)&amp;lt;&amp;lt;12) | ((c.charCodeAt(1)&amp;amp;0x3f)&amp;lt;&amp;lt;6) | ( c.charCodeAt(2)&amp;amp;0x3f); return String.fromCharCode(cc); } ); return str; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ </code></pre> PHP <pre><code>/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* aes.php Copyright © 2005-2008 Chris Veness. Right of free use is granted for all * commercial or non-commercial use. No warranty of any form is offered. */ /** * AES Cipher function: encrypt 'input' with Rijndael algorithm * * @param input message as byte-array (16 bytes) * @param w key schedule as 2D byte-array (Nr+1 x Nb bytes) - * generated from the cipher key by KeyExpansion() * @return ciphertext as byte-array (16 bytes) */ function Cipher($input, $w) { // main Cipher function [§5.1] $Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) $Nr = count($w)/$Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys $state = array(); // initialise 4xNb byte-array 'state' with input [§3.4] for ($i=0; $i&amp;lt;4*$Nb; $i++) $state[$i%4][floor($i/4)] = $input[$i]; $state = AddRoundKey($state, $w, 0, $Nb); for ($round=1; $round&amp;lt;$Nr; $round++) { // apply Nr rounds $state = SubBytes($state, $Nb); $state = ShiftRows($state, $Nb); $state = MixColumns($state, $Nb); $state = AddRoundKey($state, $w, $round, $Nb); } $state = SubBytes($state, $Nb); $state = ShiftRows($state, $Nb); $state = AddRoundKey($state, $w, $Nr, $Nb); $output = array(4*$Nb); // convert state to 1-d array before returning [§3.4] for ($i=0; $i&amp;lt;4*$Nb; $i++) $output[$i] = $state[$i%4][floor($i/4)]; return $output; } function AddRoundKey($state, $w, $rnd, $Nb) { // xor Round Key into state S [§5.1.4] for ($r=0; $r&amp;lt;4; $r++) { for ($c=0; $c&amp;lt;$Nb; $c++) $state[$r][$c] ^= $w[$rnd*4+$c][$r]; } return $state; } function SubBytes($s, $Nb) { // apply SBox to state S [§5.1.1] global $Sbox; // PHP needs explicit declaration to access global variables! for ($r=0; $r&amp;lt;4; $r++) { for ($c=0; $c&amp;lt;$Nb; $c++) $s[$r][$c] = $Sbox[$s[$r][$c]]; } return $s; } function ShiftRows($s, $Nb) { // shift row r of state S left by r bytes [§5.1.2] $t = array(4); for ($r=1; $r&amp;lt;4; $r++) { for ($c=0; $c&amp;lt;4; $c++) $t[$c] = $s[$r][($c+$r)%$Nb]; // shift into temp copy for ($c=0; $c&amp;lt;4; $c++) $s[$r][$c] = $t[$c]; // and copy back } // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES): return $s; // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf } function MixColumns($s, $Nb) { // combine bytes of each col of state S [§5.1.3] for ($c=0; $c&amp;lt;4; $c++) { $a = array(4); // 'a' is a copy of the current column from 's' $b = array(4); // 'b' is a•{02} in GF(2^8) for ($i=0; $i&amp;lt;4; $i++) { $a[$i] = $s[$i][$c]; $b[$i] = $s[$i][$c]&amp;amp;0x80 ? $s[$i][$c]&amp;lt;&amp;lt;1 ^ 0x011b : $s[$i][$c]&amp;lt;&amp;lt;1; } // a[n] ^ b[n] is a•{03} in GF(2^8) $s[0][$c] = $b[0] ^ $a[1] ^ $b[1] ^ $a[2] ^ $a[3]; // 2*a0 + 3*a1 + a2 + a3 $s[1][$c] = $a[0] ^ $b[1] ^ $a[2] ^ $b[2] ^ $a[3]; // a0 * 2*a1 + 3*a2 + a3 $s[2][$c] = $a[0] ^ $a[1] ^ $b[2] ^ $a[3] ^ $b[3]; // a0 + a1 + 2*a2 + 3*a3 $s[3][$c] = $a[0] ^ $b[0] ^ $a[1] ^ $a[2] ^ $b[3]; // 3*a0 + a1 + a2 + 2*a3 } return $s; } /** * Key expansion for Rijndael Cipher(): performs key expansion on cipher key * to generate a key schedule * * @param key cipher key byte-array (16 bytes) * @return key schedule as 2D byte-array (Nr+1 x Nb bytes) */ function KeyExpansion($key) { // generate Key Schedule from Cipher Key [§5.2] global $Rcon; // PHP needs explicit declaration to access global variables! $Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) $Nk = count($key)/4; // key length (in words): 4/6/8 for 128/192/256-bit keys $Nr = $Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys $w = array(); $temp = array(); for ($i=0; $i&amp;lt;$Nk; $i++) { $r = array($key[4*$i], $key[4*$i+1], $key[4*$i+2], $key[4*$i+3]); $w[$i] = $r; } for ($i=$Nk; $i&amp;lt;($Nb*($Nr+1)); $i++) { $w[$i] = array(); for ($t=0; $t&amp;lt;4; $t++) $temp[$t] = $w[$i-1][$t]; if ($i % $Nk == 0) { $temp = SubWord(RotWord($temp)); for ($t=0; $t&amp;lt;4; $t++) $temp[$t] ^= $Rcon[$i/$Nk][$t]; } else if ($Nk &amp;gt; 6 &amp;amp;&amp;amp; $i%$Nk == 4) { $temp = SubWord($temp); } for ($t=0; $t&amp;lt;4; $t++) $w[$i][$t] = $w[$i-$Nk][$t] ^ $temp[$t]; } return $w; } function SubWord($w) { // apply SBox to 4-byte word w global $Sbox; // PHP needs explicit declaration to access global variables! for ($i=0; $i&amp;lt;4; $i++) $w[$i] = $Sbox[$w[$i]]; return $w; } function RotWord($w) { // rotate 4-byte word w left by one byte $w[4] = $w[0]; for ($i=0; $i&amp;lt;4; $i++) $w[$i] = $w[$i+1]; return $w; } // Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1] $Sbox = array(0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16); // Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2] $Rcon = array( array(0x00, 0x00, 0x00, 0x00), array(0x01, 0x00, 0x00, 0x00), array(0x02, 0x00, 0x00, 0x00), array(0x04, 0x00, 0x00, 0x00), array(0x08, 0x00, 0x00, 0x00), array(0x10, 0x00, 0x00, 0x00), array(0x20, 0x00, 0x00, 0x00), array(0x40, 0x00, 0x00, 0x00), array(0x80, 0x00, 0x00, 0x00), array(0x1b, 0x00, 0x00, 0x00), array(0x36, 0x00, 0x00, 0x00) ); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Encrypt a text using AES encryption in Counter mode of operation * - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf * * Unicode multi-byte character safe * * @param plaintext source text to be encrypted * @param password the password to use to generate a key * @param nBits number of bits to be used in the key (128, 192, or 256) * @return encrypted text */ function AESEncryptCtr($plaintext, $password, $nBits) { $blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES if (!($nBits==128 || $nBits==192 || $nBits==256)) return ''; // standard allows 128/192/256 bit keys // note PHP (5) gives us plaintext and password in UTF8 encoding! // use AES itself to encrypt password to get cipher key (using plain password as source for key // expansion) - gives us well encrypted key $nBytes = $nBits/8; // no bytes in key $pwBytes = array(); for ($i=0; $i&amp;lt;$nBytes; $i++) $pwBytes[$i] = ord(substr($password,$i,1)) &amp;amp; 0xff; $key = Cipher($pwBytes, KeyExpansion($pwBytes)); $key = array_merge($key, array_slice($key, 0, $nBytes-16)); // expand key to 16/24/32 bytes long // initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in // 1st 8 bytes, block counter in 2nd 8 bytes $counterBlock = array(); $nonce = floor(microtime(true)*1000); // timestamp: milliseconds since 1-Jan-1970 $nonceSec = floor($nonce/1000); $nonceMs = $nonce%1000; // encode nonce with seconds in 1st 4 bytes, and (repeated) ms part filling 2nd 4 bytes for ($i=0; $i&amp;lt;4; $i++) $counterBlock[$i] = urs($nonceSec, $i*8) &amp;amp; 0xff; for ($i=0; $i&amp;lt;4; $i++) $counterBlock[$i+4] = $nonceMs &amp;amp; 0xff; // and convert it to a string to go on the front of the ciphertext $ctrTxt = ''; for ($i=0; $i&amp;lt;8; $i++) $ctrTxt .= chr($counterBlock[$i]); // generate key schedule - an expansion of the key into distinct Key Rounds for each round $keySchedule = KeyExpansion($key); $blockCount = ceil(strlen($plaintext)/$blockSize); $ciphertxt = array(); // ciphertext as array of strings for ($b=0; $b&amp;lt;$blockCount; $b++) { // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) // done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB) for ($c=0; $c&amp;lt;4; $c++) $counterBlock[15-$c] = urs($b, $c*8) &amp;amp; 0xff; for ($c=0; $c&amp;lt;4; $c++) $counterBlock[15-$c-4] = urs($b/0x100000000, $c*8); $cipherCntr = Cipher($counterBlock, $keySchedule); // -- encrypt counter block -- // block size is reduced on final block $blockLength = $b&amp;lt;$blockCount-1 ? $blockSize : (strlen($plaintext)-1)%$blockSize+1; $cipherByte = array(); for ($i=0; $i&amp;lt;$blockLength; $i++) { // -- xor plaintext with ciphered counter byte-by-byte -- $cipherByte[$i] = $cipherCntr[$i] ^ ord(substr($plaintext, $b*$blockSize+$i, 1)); $cipherByte[$i] = chr($cipherByte[$i]); } $ciphertxt[$b] = implode('', $cipherByte); // escape troublesome characters in ciphertext } // implode is more efficient than repeated string concatenation $ciphertext = $ctrTxt . implode('', $ciphertxt); $ciphertext = base64_encode($ciphertext); return $ciphertext; } /** * Decrypt a text encrypted by AES in counter mode of operation * * @param ciphertext source text to be decrypted * @param password the password to use to generate a key * @param nBits number of bits to be used in the key (128, 192, or 256) * @return decrypted text */ function AESDecryptCtr($ciphertext, $password, $nBits) { $blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES if (!($nBits==128 || $nBits==192 || $nBits==256)) return ''; // standard allows 128/192/256 bit keys $ciphertext = base64_decode($ciphertext); // use AES to encrypt password (mirroring encrypt routine) $nBytes = $nBits/8; // no bytes in key $pwBytes = array(); for ($i=0; $i&amp;lt;$nBytes; $i++) $pwBytes[$i] = ord(substr($password,$i,1)) &amp;amp; 0xff; $key = Cipher($pwBytes, KeyExpansion($pwBytes)); $key = array_merge($key, array_slice($key, 0, $nBytes-16)); // expand key to 16/24/32 bytes long // recover nonce from 1st element of ciphertext $counterBlock = array(); $ctrTxt = substr($ciphertext, 0, 8); for ($i=0; $i&amp;lt;8; $i++) $counterBlock[$i] = ord(substr($ctrTxt,$i,1)); // generate key schedule $keySchedule = KeyExpansion($key); // separate ciphertext into blocks (skipping past initial 8 bytes) $nBlocks = ceil((strlen($ciphertext)-8) / $blockSize); $ct = array(); for ($b=0; $b&amp;lt;$nBlocks; $b++) $ct[$b] = substr($ciphertext, 8+$b*$blockSize, 16); $ciphertext = $ct; // ciphertext is now array of block-length strings // plaintext will get generated block-by-block into array of block-length strings $plaintxt = array(); for ($b=0; $b&amp;lt;$nBlocks; $b++) { // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) for ($c=0; $c&amp;lt;4; $c++) $counterBlock[15-$c] = urs($b, $c*8) &amp;amp; 0xff; for ($c=0; $c&amp;lt;4; $c++) $counterBlock[15-$c-4] = urs(($b+1)/0x100000000-1, $c*8) &amp;amp; 0xff; $cipherCntr = Cipher($counterBlock, $keySchedule); // encrypt counter block $plaintxtByte = array(); for ($i=0; $i&amp;lt;strlen($ciphertext[$b]); $i++) { // -- xor plaintext with ciphered counter byte-by-byte -- $plaintxtByte[$i] = $cipherCntr[$i] ^ ord(substr($ciphertext[$b],$i,1)); $plaintxtByte[$i] = chr($plaintxtByte[$i]); } $plaintxt[$b] = implode('', $plaintxtByte); } // join array of blocks into single plaintext string $plaintext = implode('',$plaintxt); return $plaintext; } /* * Unsigned right shift function, since PHP has neither &amp;gt;&amp;gt;&amp;gt; operator nor unsigned ints * * @param a number to be shifted (32-bit integer) * @param b number of bits to shift a to the right (0..31) * @return a right-shifted and zero-filled by b bits */ function urs($a, $b) { $a &amp;amp;= 0xffffffff; $b &amp;amp;= 0x1f; // (bounds check) if ($a&amp;amp;0x80000000 &amp;amp;&amp;amp; $b&amp;gt;0) { // if left-most bit set $a = ($a&amp;gt;&amp;gt;1) &amp;amp; 0x7fffffff; // right-shift one bit &amp;amp; clear left-most bit $a = $a &amp;gt;&amp;gt; ($b-1); // remaining right-shifts } else { // otherwise $a = ($a&amp;gt;&amp;gt;$b); // use normal right-shift } return $a; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ </code></pre> <hr /> <strong> <a href="http://kevin.vanzonneveld.net" rel="nofollow">Kevin van Zonneveld</a> </strong> on 2008-09-05 21:28:49 <br /> @ Onno Marsman: It's also less readable (we have compressors for short code ;) and maintainable, have you benchmarked it against the current version? Cause if it's actually a lot faster, that would convince me to use this version. And what about copyrights? Thank you for time &amp;amp; effort Onno! <hr /> <strong> Onno Marsman </strong> on 2008-09-04 16:34:13 <br /> Found the code below somewhere. Is similar but shorter and probably a bit faster. I've worked with it for quite some time and it seems to work fine in combination with sha1 from php. Why is utf8_encode necessary by the way? Also noticed this in base64_encode. <pre><code> function sha1(s){ function X(x,y){var l=(x&amp;amp;0xFFFF)+(y&amp;amp;0xFFFF),m=(x&amp;gt;&amp;gt;16)+(y&amp;gt;&amp;gt;16)+(l&amp;gt;&amp;gt;16);return(m&amp;lt;&amp;lt;16)|(l&amp;amp;0xFFFF)} function Y(x,y){return(x&amp;lt;&amp;lt;y)|(x&amp;gt;&amp;gt;&amp;gt;(32-y))} var len=s.length*8,i,L=((len+64&amp;gt;&amp;gt;9)&amp;lt;&amp;lt;4)+16,x=Array(L+79),w=Array(80),a=1732584193,b=-271733879,c=-1732584194,d=271733878,e=-1009589776; for(i=0;i&amp;lt;x.length;++i)x[i]=0; for(i=0;i&amp;lt;len;i+=8)x[i&amp;gt;&amp;gt;5]|=(s.charCodeAt(i/8)&amp;amp;255)&amp;lt;&amp;lt;(24-i%32); x[len&amp;gt;&amp;gt;5]|=0x80&amp;lt;&amp;lt;(24-len%32); x[L-1]=len; for(i=0;i&amp;lt;L;i+=16){ var oa=a,ob=b,oc=c,od=d,oe=e; for(var j=0;j&amp;lt;80;j++){ w[j]=(j&amp;lt;16)?x[i+j]:Y(w[j-3]^w[j-8]^w[j-14]^w[j-16],1); var t=X(X(Y(a,5),((j&amp;lt;20)?((b&amp;amp;c)|((~b)&amp;amp;d)):((j&amp;lt;40||j&amp;gt;=60)?(b^c^d):((b&amp;amp;c)|(b&amp;amp;d)|(c&amp;amp;d))))),X(X(e,w[j]),((j&amp;lt;20)?1518500249:(j&amp;lt;40)?1859775393:(j&amp;lt;60)?-1894007588:-899497514))); e=d; d=c; c=Y(b,30); b=a; a=t; } a=X(a,oa); b=X(b,ob); c=X(c,oc); d=X(d,od); e=X(e,oe); } x=[a,b,c,d,e]; a=&amp;quot;0123456789abcdef&amp;quot;; b=&amp;quot;&amp;quot;; for(i=0;i&amp;lt;20;i++)b+=a.charAt((x[i&amp;gt;&amp;gt;2]&amp;gt;&amp;gt;((3-i%4)*8+4))&amp;amp;0xF)+a.charAt((x[i&amp;gt;&amp;gt;2]&amp;gt;&amp;gt;((3-i%4)*8))&amp;amp;0xF); return b } </code></pre> <hr />