js/src/jit-test/tests/sunspider/check-crypto-aes.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
     3 /*
     4  * AES Cipher function: encrypt 'input' with Rijndael algorithm
     5  *
     6  *   takes   byte-array 'input' (16 bytes)
     7  *           2D byte-array key schedule 'w' (Nr+1 x Nb bytes)
     8  *
     9  *   applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage
    10  *
    11  *   returns byte-array encrypted value (16 bytes)
    12  */
    13 function Cipher(input, w) {    // main Cipher function [§5.1]
    14   var Nb = 4;               // block size (in words): no of columns in state (fixed at 4 for AES)
    15   var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys
    17   var state = [[],[],[],[]];  // initialise 4xNb byte-array 'state' with input [§3.4]
    18   for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i];
    20   state = AddRoundKey(state, w, 0, Nb);
    22   for (var round=1; round<Nr; round++) {
    23     state = SubBytes(state, Nb);
    24     state = ShiftRows(state, Nb);
    25     state = MixColumns(state, Nb);
    26     state = AddRoundKey(state, w, round, Nb);
    27   }
    29   state = SubBytes(state, Nb);
    30   state = ShiftRows(state, Nb);
    31   state = AddRoundKey(state, w, Nr, Nb);
    33   var output = new Array(4*Nb);  // convert state to 1-d array before returning [§3.4]
    34   for (var i=0; i<4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)];
    35   return output;
    36 }
    39 function SubBytes(s, Nb) {    // apply SBox to state S [§5.1.1]
    40   for (var r=0; r<4; r++) {
    41     for (var c=0; c<Nb; c++) s[r][c] = Sbox[s[r][c]];
    42   }
    43   return s;
    44 }
    47 function ShiftRows(s, Nb) {    // shift row r of state S left by r bytes [§5.1.2]
    48   var t = new Array(4);
    49   for (var r=1; r<4; r++) {
    50     for (var c=0; c<4; c++) t[c] = s[r][(c+r)%Nb];  // shift into temp copy
    51     for (var c=0; c<4; c++) s[r][c] = t[c];         // and copy back
    52   }          // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
    53   return s;  // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf 
    54 }
    57 function MixColumns(s, Nb) {   // combine bytes of each col of state S [§5.1.3]
    58   for (var c=0; c<4; c++) {
    59     var a = new Array(4);  // 'a' is a copy of the current column from 's'
    60     var b = new Array(4);  // 'b' is a•{02} in GF(2^8)
    61     for (var i=0; i<4; i++) {
    62       a[i] = s[i][c];
    63       b[i] = s[i][c]&0x80 ? s[i][c]<<1 ^ 0x011b : s[i][c]<<1;
    64     }
    65     // a[n] ^ b[n] is a•{03} in GF(2^8)
    66     s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // 2*a0 + 3*a1 + a2 + a3
    67     s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 * 2*a1 + 3*a2 + a3
    68     s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + 2*a2 + 3*a3
    69     s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // 3*a0 + a1 + a2 + 2*a3
    70   }
    71   return s;
    72 }
    75 function AddRoundKey(state, w, rnd, Nb) {  // xor Round Key into state S [§5.1.4]
    76   for (var r=0; r<4; r++) {
    77     for (var c=0; c<Nb; c++) state[r][c] ^= w[rnd*4+c][r];
    78   }
    79   return state;
    80 }
    83 function KeyExpansion(key) {  // generate Key Schedule (byte-array Nr+1 x Nb) from Key [§5.2]
    84   var Nb = 4;            // block size (in words): no of columns in state (fixed at 4 for AES)
    85   var Nk = key.length/4  // key length (in words): 4/6/8 for 128/192/256-bit keys
    86   var Nr = Nk + 6;       // no of rounds: 10/12/14 for 128/192/256-bit keys
    88   var w = new Array(Nb*(Nr+1));
    89   var temp = new Array(4);
    91   for (var i=0; i<Nk; i++) {
    92     var r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]];
    93     w[i] = r;
    94   }
    96   for (var i=Nk; i<(Nb*(Nr+1)); i++) {
    97     w[i] = new Array(4);
    98     for (var t=0; t<4; t++) temp[t] = w[i-1][t];
    99     if (i % Nk == 0) {
   100       temp = SubWord(RotWord(temp));
   101       for (var t=0; t<4; t++) temp[t] ^= Rcon[i/Nk][t];
   102     } else if (Nk > 6 && i%Nk == 4) {
   103       temp = SubWord(temp);
   104     }
   105     for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t];
   106   }
   108   return w;
   109 }
   111 function SubWord(w) {    // apply SBox to 4-byte word w
   112   for (var i=0; i<4; i++) w[i] = Sbox[w[i]];
   113   return w;
   114 }
   116 function RotWord(w) {    // rotate 4-byte word w left by one byte
   117   w[4] = w[0];
   118   for (var i=0; i<4; i++) w[i] = w[i+1];
   119   return w;
   120 }
   123 // Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1]
   124 var Sbox =  [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
   125              0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
   126              0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
   127              0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
   128              0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
   129              0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
   130              0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
   131              0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
   132              0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
   133              0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
   134              0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
   135              0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
   136              0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
   137              0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
   138              0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
   139              0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16];
   141 // Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]
   142 var Rcon = [ [0x00, 0x00, 0x00, 0x00],
   143              [0x01, 0x00, 0x00, 0x00],
   144              [0x02, 0x00, 0x00, 0x00],
   145              [0x04, 0x00, 0x00, 0x00],
   146              [0x08, 0x00, 0x00, 0x00],
   147              [0x10, 0x00, 0x00, 0x00],
   148              [0x20, 0x00, 0x00, 0x00],
   149              [0x40, 0x00, 0x00, 0x00],
   150              [0x80, 0x00, 0x00, 0x00],
   151              [0x1b, 0x00, 0x00, 0x00],
   152              [0x36, 0x00, 0x00, 0x00] ]; 
   155 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
   157 /* 
   158  * Use AES to encrypt 'plaintext' with 'password' using 'nBits' key, in 'Counter' mode of operation
   159  *                           - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
   160  *   for each block
   161  *   - outputblock = cipher(counter, key)
   162  *   - cipherblock = plaintext xor outputblock
   163  */
   164 function AESEncryptCtr(plaintext, password, nBits) {
   165   if (!(nBits==128 || nBits==192 || nBits==256)) return '';  // standard allows 128/192/256 bit keys
   167   // for this example script, generate the key by applying Cipher to 1st 16/24/32 chars of password; 
   168   // for real-world applications, a more secure approach would be to hash the password e.g. with SHA-1
   169   var nBytes = nBits/8;  // no bytes in key
   170   var pwBytes = new Array(nBytes);
   171   for (var i=0; i<nBytes; i++) pwBytes[i] = password.charCodeAt(i) & 0xff;
   172   var key = Cipher(pwBytes, KeyExpansion(pwBytes));
   173   key = key.concat(key.slice(0, nBytes-16));  // key is now 16/24/32 bytes long
   175   // initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in 1st 8 bytes,
   176   // block counter in 2nd 8 bytes
   177   var blockSize = 16;  // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
   178   var counterBlock = new Array(blockSize);  // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
   179   var nonce = (new Date("2000-01-01")).getTime();  // milliseconds since 1-Jan-1970;
   180                                                    // fixed for repeatability
   182   // encode nonce in two stages to cater for JavaScript 32-bit limit on bitwise ops
   183   for (var i=0; i<4; i++) counterBlock[i] = (nonce >>> i*8) & 0xff;
   184   for (var i=0; i<4; i++) counterBlock[i+4] = (nonce/0x100000000 >>> i*8) & 0xff; 
   186   // generate key schedule - an expansion of the key into distinct Key Rounds for each round
   187   var keySchedule = KeyExpansion(key);
   189   var blockCount = Math.ceil(plaintext.length/blockSize);
   190   var ciphertext = new Array(blockCount);  // ciphertext as array of strings
   192   for (var b=0; b<blockCount; b++) {
   193     // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
   194     // again done in two stages for 32-bit ops
   195     for (var c=0; c<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff;
   196     for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8)
   198     var cipherCntr = Cipher(counterBlock, keySchedule);  // -- encrypt counter block --
   200     // calculate length of final block:
   201     var blockLength = b<blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1;
   203     var ct = '';
   204     for (var i=0; i<blockLength; i++) {  // -- xor plaintext with ciphered counter byte-by-byte --
   205       var plaintextByte = plaintext.charCodeAt(b*blockSize+i);
   206       var cipherByte = plaintextByte ^ cipherCntr[i];
   207       ct += String.fromCharCode(cipherByte);
   208     }
   209     // ct is now ciphertext for this block
   211     ciphertext[b] = escCtrlChars(ct);  // escape troublesome characters in ciphertext
   212   }
   214   // convert the nonce to a string to go on the front of the ciphertext
   215   var ctrTxt = '';
   216   for (var i=0; i<8; i++) ctrTxt += String.fromCharCode(counterBlock[i]);
   217   ctrTxt = escCtrlChars(ctrTxt);
   219   // use '-' to separate blocks, use Array.join to concatenate arrays of strings for efficiency
   220   return ctrTxt + '-' + ciphertext.join('-');
   221 }
   224 /* 
   225  * Use AES to decrypt 'ciphertext' with 'password' using 'nBits' key, in Counter mode of operation
   226  *
   227  *   for each block
   228  *   - outputblock = cipher(counter, key)
   229  *   - cipherblock = plaintext xor outputblock
   230  */
   231 function AESDecryptCtr(ciphertext, password, nBits) {
   232   if (!(nBits==128 || nBits==192 || nBits==256)) return '';  // standard allows 128/192/256 bit keys
   234   var nBytes = nBits/8;  // no bytes in key
   235   var pwBytes = new Array(nBytes);
   236   for (var i=0; i<nBytes; i++) pwBytes[i] = password.charCodeAt(i) & 0xff;
   237   var pwKeySchedule = KeyExpansion(pwBytes);
   238   var key = Cipher(pwBytes, pwKeySchedule);
   239   key = key.concat(key.slice(0, nBytes-16));  // key is now 16/24/32 bytes long
   241   var keySchedule = KeyExpansion(key);
   243   ciphertext = ciphertext.split('-');  // split ciphertext into array of block-length strings 
   245   // recover nonce from 1st element of ciphertext
   246   var blockSize = 16;  // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
   247   var counterBlock = new Array(blockSize);
   248   var ctrTxt = unescCtrlChars(ciphertext[0]);
   249   for (var i=0; i<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i);
   251   var plaintext = new Array(ciphertext.length-1);
   253   for (var b=1; b<ciphertext.length; b++) {
   254     // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
   255     for (var c=0; c<4; c++) counterBlock[15-c] = ((b-1) >>> c*8) & 0xff;
   256     for (var c=0; c<4; c++) counterBlock[15-c-4] = ((b/0x100000000-1) >>> c*8) & 0xff;
   258     var cipherCntr = Cipher(counterBlock, keySchedule);  // encrypt counter block
   260     ciphertext[b] = unescCtrlChars(ciphertext[b]);
   262     var pt = '';
   263     for (var i=0; i<ciphertext[b].length; i++) {
   264       // -- xor plaintext with ciphered counter byte-by-byte --
   265       var ciphertextByte = ciphertext[b].charCodeAt(i);
   266       var plaintextByte = ciphertextByte ^ cipherCntr[i];
   267       pt += String.fromCharCode(plaintextByte);
   268     }
   269     // pt is now plaintext for this block
   271     plaintext[b-1] = pt;  // b-1 'cos no initial nonce block in plaintext
   272   }
   274   return plaintext.join('');
   275 }
   277 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
   279 function escCtrlChars(str) {  // escape control chars which might cause problems handling ciphertext
   280   return str.replace(/[\0\t\n\v\f\r\xa0'"!-]/g, function(c) { return '!' + c.charCodeAt(0) + '!'; });
   281 }  // \xa0 to cater for bug in Firefox; include '-' to leave it free for use as a block marker
   283 function unescCtrlChars(str) {  // unescape potentially problematic control characters
   284   return str.replace(/!\d\d?\d?!/g, function(c) { return String.fromCharCode(c.slice(1,-1)); });
   285 }
   286 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
   288 /*
   289  * if escCtrlChars()/unescCtrlChars() still gives problems, use encodeBase64()/decodeBase64() instead
   290  */
   291 var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
   293 function encodeBase64(str) {  // http://tools.ietf.org/html/rfc4648
   294    var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc='';
   296    str = encodeUTF8(str);  // encode multi-byte chars into UTF-8 for byte-array
   298    do {  // pack three octets into four hexets
   299       o1 = str.charCodeAt(i++);
   300       o2 = str.charCodeAt(i++);
   301       o3 = str.charCodeAt(i++);
   303       bits = o1<<16 | o2<<8 | o3;
   305       h1 = bits>>18 & 0x3f;
   306       h2 = bits>>12 & 0x3f;
   307       h3 = bits>>6 & 0x3f;
   308       h4 = bits & 0x3f;
   310       // end of string? index to '=' in b64
   311       if (isNaN(o3)) h4 = 64;
   312       if (isNaN(o2)) h3 = 64;
   314       // use hexets to index into b64, and append result to encoded string
   315       enc += b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
   316    } while (i < str.length);
   318    return enc;
   319 }
   321 function decodeBase64(str) {
   322    var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc='';
   324    do {  // unpack four hexets into three octets using index points in b64
   325       h1 = b64.indexOf(str.charAt(i++));
   326       h2 = b64.indexOf(str.charAt(i++));
   327       h3 = b64.indexOf(str.charAt(i++));
   328       h4 = b64.indexOf(str.charAt(i++));
   330       bits = h1<<18 | h2<<12 | h3<<6 | h4;
   332       o1 = bits>>16 & 0xff;
   333       o2 = bits>>8 & 0xff;
   334       o3 = bits & 0xff;
   336       if (h3 == 64)      enc += String.fromCharCode(o1);
   337       else if (h4 == 64) enc += String.fromCharCode(o1, o2);
   338       else               enc += String.fromCharCode(o1, o2, o3);
   339    } while (i < str.length);
   341    return decodeUTF8(enc);  // decode UTF-8 byte-array back to Unicode
   342 }
   344 function encodeUTF8(str) {  // encode multi-byte string into utf-8 multiple single-byte characters 
   345   str = str.replace(
   346       /[\u0080-\u07ff]/g,  // U+0080 - U+07FF = 2-byte chars
   347       function(c) { 
   348         var cc = c.charCodeAt(0);
   349         return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); }
   350     );
   351   str = str.replace(
   352       /[\u0800-\uffff]/g,  // U+0800 - U+FFFF = 3-byte chars
   353       function(c) { 
   354         var cc = c.charCodeAt(0); 
   355         return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); }
   356     );
   357   return str;
   358 }
   360 function decodeUTF8(str) {  // decode utf-8 encoded string back into multi-byte characters
   361   str = str.replace(
   362       /[\u00c0-\u00df][\u0080-\u00bf]/g,                 // 2-byte chars
   363       function(c) { 
   364         var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f;
   365         return String.fromCharCode(cc); }
   366     );
   367   str = str.replace(
   368       /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g,  // 3-byte chars
   369       function(c) { 
   370         var cc = (c.charCodeAt(0)&0x0f)<<12 | (c.charCodeAt(1)&0x3f<<6) | c.charCodeAt(2)&0x3f; 
   371         return String.fromCharCode(cc); }
   372     );
   373   return str;
   374 }
   377 function byteArrayToHexStr(b) {  // convert byte array to hex string for displaying test vectors
   378   var s = '';
   379   for (var i=0; i<b.length; i++) s += b[i].toString(16) + ' ';
   380   return s;
   381 }
   383 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
   386 var plainText = "ROMEO: But, soft! what light through yonder window breaks?\n\
   387 It is the east, and Juliet is the sun.\n\
   388 Arise, fair sun, and kill the envious moon,\n\
   389 Who is already sick and pale with grief,\n\
   390 That thou her maid art far more fair than she:\n\
   391 Be not her maid, since she is envious;\n\
   392 Her vestal livery is but sick and green\n\
   393 And none but fools do wear it; cast it off.\n\
   394 It is my lady, O, it is my love!\n\
   395 O, that she knew she were!\n\
   396 She speaks yet she says nothing: what of that?\n\
   397 Her eye discourses; I will answer it.\n\
   398 I am too bold, 'tis not to me she speaks:\n\
   399 Two of the fairest stars in all the heaven,\n\
   400 Having some business, do entreat her eyes\n\
   401 To twinkle in their spheres till they return.\n\
   402 What if her eyes were there, they in her head?\n\
   403 The brightness of her cheek would shame those stars,\n\
   404 As daylight doth a lamp; her eyes in heaven\n\
   405 Would through the airy region stream so bright\n\
   406 That birds would sing and think it were not night.\n\
   407 See, how she leans her cheek upon her hand!\n\
   408 O, that I were a glove upon that hand,\n\
   409 That I might touch that cheek!\n\
   410 JULIET: Ay me!\n\
   411 ROMEO: She speaks:\n\
   412 O, speak again, bright angel! for thou art\n\
   413 As glorious to this night, being o'er my head\n\
   414 As is a winged messenger of heaven\n\
   415 Unto the white-upturned wondering eyes\n\
   416 Of mortals that fall back to gaze on him\n\
   417 When he bestrides the lazy-pacing clouds\n\
   418 And sails upon the bosom of the air.";
   420 var password = "O Romeo, Romeo! wherefore art thou Romeo?";
   422 var cipherText = AESEncryptCtr(plainText, password, 256);
   423 var decryptedText = AESDecryptCtr(cipherText, password, 256);
   425 assertEq(plainText, decryptedText);

mercurial