Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web development, networking and server security. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

AES Advanced Encryption Standard

Source: http://www.movable-type.co.uk/scripts/aes.html



AES este un "cifru bloc simetric" pentru criptarea texte care pot fi decriptate cu cheia de criptare originale.

In mai multe scopuri, un algoritm de criptare mai simple, cum ar fi TEA este perfect adecvată - dar dacă bănuiţi că aveţi din lume cele mai strălucite minţi criptografice, şi câteva milioane de dolari a resurselor de calcul, ar putea fi incearca sa sparga dvs. de securitate, apoi AES, pe baza Rijndael algoritmul, este de securitate mai strânse disponibile în prezent (aprobat de către guvernul Statelor Unite pentru informaţii clasificate până la "secret" - şi în în 192 sau 256 lungimi-cheie, în sus "Top Secret" a). AES a fost adoptat de către NIST în 2001, ca FIPS-197, şi este de înlocuire pentru DES, care a fost retras în 2005.

Am dezvoltat această punere în aplicare JavaScript pentru a ilustra original AES standard (NIST FIPS-197), cât mai strâns posibil. Aceasta este concepută ca o introducere pentru cei care doresc să înveţe ceva despre punerea în aplicare de criptare nu, o punere în aplicare de autoritate - experţi criptografie va şti deja mai mult decât voi prezenta aici.Accentul se pune pe transparenţă şi fidelitatea faţă de standard, mai degrabă decât de eficienţă.

Acest script include, de asemenea o funcţie de înveliş, care pune în aplicare AES în "Counter" modul de funcţionare(specificate în NIST SP 800-38A) pentru a cripta texte arbitrare - descrieri mulţi dintre AES se limiteze la rutina Cipher în sine, şi nu ia în considerare modul în care acesta poate fi folosit pentru a cripta texte.

 

O mare parte din algoritmul Rijndael se bazează pe aritmetica pe un câmp finit, sau câmp Galois (după matematician).Aritmetica regulate funcţionează pe o gamă infinită de numere - dublarea păstrează un număr şi-l va primi tot mai mari.Aritmetică într-un domeniu finit este limitat la numerele în acest domeniu. Algoritmul de Rijndael fabrică în GF (2 8), în care rezultatele aritmetice pot fi depozitate întotdeauna în termen de un octet - care este destul de convenabil pentru computere. Nu pot începe să înţeleagă matematicii (având în vedere că adunarea şi scăderea sunt acelasi lucru - un XORoperaţie - şi multiplicare se face "modulo un polinom ireductibil": dublarea 0x80 în GF (2 8) dă 0x1b).

Algoritmul Rijndael se pretează la implementarea pe scară largă diferite, deoarece matematica poate fi codificate în mod direct, sau pre-calculate ca tabele de căutare - în mod direct în paralel cu utilizarea de tabele jurnal pentru aritmetica.Implementări diferite pot avea diferite cu plata-off între viteză, complexitatea şi cerinţele de stocare. Unele pot semana abia reciproc. În această punere în aplicare, am urmat îndeaproape forfetare, ca pe standard, am folosit un tabel de căutare (" S-box ") pentru punerea în aplicare a inversa multiplicativ (adică 1 / x) într-un câmp finit (utilizat pentru transformarea SubBytes), dar alte calcule sunt făcute în mod direct, mai degrabă decât să fie pre-calculate.

Daca vrei sa te convingi că funcţia Cipher funcţionează în mod corespunzător pe plan intern (si ar trebui sa!), NIST prevăd vectori de test pentru AES (anexa C.1 din standard). Clic

şi blocul de ieşire cifrul ar trebui să fie

(În modul contra, un text ar putea decripta corect, chiar dacă a fost viciat de rutină cifru).

Cifrul Inverse este în mare măsură o oglindă de rutină Cipher, cu funcţii în paralel pentru Cipher, SubBytes şi ShiftRows.Rutină MixColumns este uşor mai complexe în invers. Nu am pus în aplicare cifrul invers aici, deoarece nu este necesară în mod contra.


Modul de operare Counter: standard AES se preocupa cu date numerice sau binare (Rijndael, impreuna cu cele mai multe algoritmi de criptare alte, lucreaza pe un bloc fix-size de numere - în cazul de AES, fiecare bloc este de 128 de biti sau 16 bytes).

În scopul de a face uz de ea pentru a cripta lucruri reale (cum ar fi texte), trebuie să fie utilizate într-un anumit " mod de operare ". Aceasta este interfaţa dintre text sau fişiere, şi algoritmul de criptare pur numeric. A se vedea NIST publicaţie specială SP800-38A pentru mai multe detalii şi a vectorilor de test.

Cel mai simplu mod de funcţionare ("dictionar electronic") criptează un bloc de text-de-bloc - dar, pentru că acelaşi bloc de text clar va genera întotdeauna acelaşi bloc de text cifrat, acest lucru poate lăsa prea multe indicii pentru atacatori.

În " modul contra "folosit în această punere în aplicare, un contor care se schimbă odată cu fiecare bloc este, în primul criptate, iar rezultatul este la nivel de bit xor d "cu blocul plaintext pentru a obţine bloc cifrat (deci plaintext nu este, de fapt direct criptate). Un unic " nDupă "este încorporată în contra pentru a se asigura ciphertexts diferite sunt întotdeauna generate de plaintext acelaşi de fiecare dată când acestea sunt criptate, acest număr este stocat în fruntea cifrat pentru a permite decriptare. O combinaţie de secunde de la unu ianuarie 1970, o milisecunda-timestamp, şi un număr de sub-milisecunde aleatoare oferă o nDupă foarte eficient. (Pentru a rezista atacurilor criptografice, nDupă nu trebuie să fie secrete sau imprevizibilă, dar este imperativ ca acesta este unic). În această punere în aplicare, blocul iniţial deţine nDupă în primele 8 octeţi, iar blocul conta în al doilea 8 octeţi. Deoarece JavaScript pot reprezenta numere întregi până la 2 53, acest lucru permite o dimensiune mesaj de până la 2 57 (c. 10 17) octeţi - puţin probabil să fie o limitare! Reţineţi că nDupă în modul contor este echivalentul a vectorului de iniţializare (IV), în alte moduri de operare.

O calitate curios de modul contor este faptul că utilizează decriptarea, de asemenea, algoritmul cifrul, mai degrabă decât invers algoritmul-cifrul. Deşi simplu să pună în aplicare, sa stabilit a fi foarte sigur.

Criptarea texte sau fişiere nu necesită numai de modul de operare. Atunci când punerea în aplicare a AES, va trebui să ia în considerare

Cheie în acest scenariu este obţinută prin aplicarea de rutină Cipher pentru a cripta primele 16/24/32 caractere ale parolei (pentru chei 128-/192-/256-bit) pentru a face cheie. Aceasta este o modalitate convenabilă de a obţine o cheie de sigur într-un script în întregime de sine stătătoare (într-un mediu de producţie, spre deosebire de prezentul cod, în esenţă tutorial, cheie ar putea fi generate ca un hash, de exemplu, pur şi simplu tasta = Sha256 (parola)). În mai multe detalii, parola furnizată este convertit la UTF-8 (care urmează să fie byte-sigur), apoi primele 16/24/32 caractere sunt transformate în octeţi. PwBytes rezultat este folosit ca o sămânţă pentru Aes.keyExpansion (), care este apoi utilizat drept cheie pentru a cripta pwBytes cu Aes.cipher (). Exemple de chei generate în acest mod de la (nerealist) parole simple:

"A" (U 0061): pwBytes = 61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
key = Dd 60 84 49 14 05 7b 5d f8 7a e3 81 B9 0e E7 dd
"B" (U 0062): pwBytes = 62 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
key = b4 1a 83 4f da 4b aa 41 76 62 fi d6 2c 66 83 6d
"☺" (U 263 a): pwBytes = e2 ba 98 00 00 00 00 00 00 00 00 00 00 00 00 00
key = d1 0C cd fd 44 45 54 EF 59 f8 aa dc 78 8e 7c 9a

Chiar si cu o diferenta de un singur bit între două parole ('a' şi 'b'), cheia este complet diferit.

 

Utilizare: această punere în aplicare ar putea fi invocat, după cum urmează:

  var parola =  'L0ck-l saf3 " ;     var plaintext =  Pssst... đon't spune                                               

Are sens să pună în aplicare AES în JavaScript? Uneori, JavaScript poate fi utilizat pentru cererile de criptografice din lumea reală (în special cele bazate pe web). Şi o punere în aplicare JavaScript, pe care oricine poate juca în jurul valorii de cu, poate oferi un simplu punct de plecare pentru punerea în aplicare în alte limbi. Deşi eu încă mai cred că TEA este în general destul de bun pentru aplicatii simple, şi un mare simplu de utilizat.

Vectorii de testare au fost confirmate în IE 6 & 7 (Win), FF 2 şi 3 (Win), Safari 2 (Mac), Opera 9 (Linux), şi Konqueror 3 (Linux) (thx Jon Passki, Vincenzo Buttazzo). Dacă puteţi confirma orice alte versiuni (sau găsit probleme), vă rog să-mi spuneţi!

În alte limbi: Am dezvoltat o versiune PHP care reflectă în mod direct această versiune JavaScript-ului, aceasta este diferită, în care PHP a Base64 codare şi codificarea UTF-8 built-in, şi nu are nici nesemnate-dreapta-Shift operator, dar este (!) altfel un simplu port. În alte limbi, să fie sigur de a utiliza pe 64 de biţi întregi / tânjeşte, fie nesemnate sau nesemnate, cu dreapta-Shift operatori; posibil să aveţi nevoie să ia în considerare modul în diferite limbi mâner op la nivel de bit, şi de probleme de standard, desigur, cum ar fi manipularea şi matrice stricte de dactilografiere. Eu nu sunt la curent cu orice alte probleme.

Viteza: - (!) După cum sa menţionat, aceasta nu este o punere în aplicare optimizat pe un procesor Intel Core 2 2GHz maşină, această proceselor de implementare în jurul valorii de 6kB/sec folosind IE7, şi 30kB/sec folosind FF3 (o pagină de text la un standard de 250 de cuvinte este despre 1.5kB). Versiunea de 128-biţi este de circa 25% mai rapid decât versiunea 256-biţi.

Pentru mai multe informaţii, au o privire la

Pentru unele aplicaţii de securitate, un hash criptografic este mai adecvat decât criptare - daca sunteti interesati intr-o functie hash, a se vedea implementari meu de SHA-1 şi SHA-256.

Octombrie 2009: Am actualizat formularea acestor script-uri de a utiliza JavaScript spaţii de nume pentru încapsulare mai bine numele de functii.


Vezi mai jos pentru codul sursă al punerii în aplicare JavaScript. § număr de ec ţ iunea se referă codul înapoi la secţiunile în standard.

Creative Commons LicenseOfer aceste Formule şi script-uri pentru utilizare gratuită şi adaptarea la acestea, contribuţia mea la open-source info-sferă de la care le-am primit atât de mult. Sunteţi bineveniţi să re-utilizeze aceste script-uri [sub o simplă atribuire de licenţă, fără nici o garanţie expresă sau implicită], cu condiţia exclusiv pe care îl păstra anunţul meu drepturile de autor şi un link către această pagină.

Paypal donareDacă doriţi să arate aprecierea dumneavoastră şi a sprijini dezvoltarea în continuare a acestor script-uri, aş mai accepta cu recunoştinţă donaţii.

Notă: acest script a fost revizuită la o august 2008 să utilizeze codificarea Base64 - versiunea anterioară, care a folosit mai puţin codare standard, este încă disponibil în cazul în care aveţi nevoie pentru a se referi înapoi la ea.

Dacă aveţi orice întrebări sau orice probleme, contactati-ma la ku.oc.epyt-elbavom @ CNE-stpircs.

© 2005-2011 Chris Veness

 


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/*  AES implementation in JavaScript (c) Chris Veness 2005-2011                                   */
/*   - see http://csrc.nist.gov/publications/PubsFIPS.html#197                                    */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

var Aes = {};  // Aes namespace

/**
 * AES Cipher function: encrypt 'input' state with Rijndael algorithm
 *   applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage
 *
 * @param {Number[]} input 16-byte (128-bit) input state array
 * @param {Number[][]} w   Key schedule as 2D byte-array (Nr+1 x Nb bytes)
 * @returns {Number[]}     Encrypted output state array
 */
Aes.cipher = function(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<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i];

  state = Aes.addRoundKey(state, w, 0, Nb);

  for (var round=1; round<Nr; round++) {
    state = Aes.subBytes(state, Nb);
    state = Aes.shiftRows(state, Nb);
    state = Aes.mixColumns(state, Nb);
    state = Aes.addRoundKey(state, w, round, Nb);
  }

  state = Aes.subBytes(state, Nb);
  state = Aes.shiftRows(state, Nb);
  state = Aes.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<4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)];
  return output;
}

/**
 * Perform Key Expansion to generate a Key Schedule
 *
 * @param {Number[]} key Key as 16/24/32-byte array
 * @returns {Number[][]} Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes)
 */
Aes.keyExpansion = function(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<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<(Nb*(Nr+1)); i++) {
    w[i] = new Array(4);
    for (var t=0; t<4; t++) temp[t] = w[i-1][t];
    if (i % Nk == 0) {
      temp = Aes.subWord(Aes.rotWord(temp));
      for (var t=0; t<4; t++) temp[t] ^= Aes.rCon[i/Nk][t];
    } else if (Nk > 6 && i%Nk == 4) {
      temp = Aes.subWord(temp);
    }
    for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t];
  }

  return w;
}

/*
 * ---- remaining routines are private, not called externally ----
 */
 
Aes.subBytes = function(s, Nb) {    // apply SBox to state S [§5.1.1]
  for (var r=0; r<4; r++) {
    for (var c=0; c<Nb; c++) s[r][c] = Aes.sBox[s[r][c]];
  }
  return s;
}

Aes.shiftRows = function(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<4; r++) {
    for (var c=0; c<4; c++) t[c] = s[r][(c+r)%Nb];  // shift into temp copy
    for (var c=0; c<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 asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
}

Aes.mixColumns = function(s, Nb) {   // combine bytes of each col of state S [§5.1.3]
  for (var c=0; c<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<4; i++) {
      a[i] = s[i][c];
      b[i] = s[i][c]&0x80? s[i][c]<<1 ^ 0x011b: s[i][c]<<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;
}

Aes.addRoundKey = function(state, w, rnd, Nb) {  // xor Round Key into state S [§5.1.4]
  for (var r=0; r<4; r++) {
    for (var c=0; c<Nb; c++) state[r][c] ^= w[rnd*4+c][r];
  }
  return state;
}

Aes.subWord = function(w) {    // apply SBox to 4-byte word w
  for (var i=0; i<4; i++) w[i] = Aes.sBox[w[i]];
  return w;
}

Aes.rotWord = function(w) {    // rotate 4-byte word w left by one byte
  var tmp = w[0];
  for (var i=0; i<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]
Aes.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]
Aes.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]]; 


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/*  AES Counter-mode implementation in JavaScript (c) Chris Veness 2005-2011                      */
/*   - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf                       */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

Aes.Ctr = {};  // Aes.Ctr namespace: a subclass or extension of Aes

/** 
 * Encrypt a text using AES encryption in Counter mode of operation
 *
 * Unicode multi-byte character safe
 *
 * @param {String} plaintext Source text to be encrypted
 * @param {String} password  The password to use to generate a key
 * @param {Number} nBits     Number of bits to be used in the key (128, 192, or 256)
 * @returns {string}         Encrypted text
 */
Aes.Ctr.encrypt = function(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 = Utf8.encode(plaintext);
  password = Utf8.encode(password);
  //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 (though hashed key might be preferred for prod'n use)
  var nBytes = nBits/8;  // no bytes in key (16/24/32)
  var pwBytes = new Array(nBytes);
  for (var i=0; i<nBytes; i++) {  // use 1st 16/24/32 chars of password for key
    pwBytes[i] = isNaN(password.charCodeAt(i))? 0: password.charCodeAt(i);
  }
  var key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes));  // gives us 16-byte key
  key = key.concat(key.slice(0, nBytes-16));  // expand key to 16/24/32 bytes long

  // initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec, 
  // [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106
  var counterBlock = new Array(blockSize);
  
  var nonce = (new Date()).getTime();  // timestamp: milliseconds since 1-Jan-1970
  var nonceMs = nonce%1000;
  var nonceSec = Math.floor(nonce/1000);
  var nonceRnd = Math.floor(Math.random()*0xffff);
  
  for (var i=0; i<2; i++) counterBlock[i]   = (nonceMs  >>> i*8) & 0xff;
  for (var i=0; i<2; i++) counterBlock[i+2] = (nonceRnd >>> i*8) & 0xff;
  for (var i=0; i<4; i++) counterBlock[i+4] = (nonceSec >>> i*8) & 0xff;
  
  // and convert it to a string to go on the front of the ciphertext
  var ctrTxt = '';
  for (var i=0; i<8; i++) ctrTxt+= String.fromCharCode(counterBlock[i]);

  // generate key schedule - an expansion of the key into distinct Key Rounds for each round
  var keySchedule = Aes.keyExpansion(key);
  
  var blockCount = Math.ceil(plaintext.length/blockSize);
  var ciphertxt = new Array(blockCount);  // ciphertext as array of strings
  
  for (var b=0; b<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<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff;
    for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8)

    var cipherCntr = Aes.cipher(counterBlock, keySchedule);  // -- encrypt counter block --
    
    // block size is reduced on final block
    var blockLength = b<blockCount-1? blockSize: (plaintext.length-1)%blockSize+1;
    var cipherChar = new Array(blockLength);
    
    for (var i=0; i<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 in IE
  var ciphertext = ctrTxt+ ciphertxt.join('');
  ciphertext = Base64.encode(ciphertext);  // encode in base64
  
  //alert((new Date()) - t);
  return ciphertext;
}

/** 
 * Decrypt a text encrypted by AES in counter mode of operation
 *
 * @param {String} ciphertext Source text to be encrypted
 * @param {String} password   The password to use to generate a key
 * @param {Number} nBits      Number of bits to be used in the key (128, 192, or 256)
 * @returns {String}          Decrypted text
 */
Aes.Ctr.decrypt = function(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 = Base64.decode(ciphertext);
  password = Utf8.encode(password);
  //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<nBytes; i++) {
    pwBytes[i] = isNaN(password.charCodeAt(i))? 0: password.charCodeAt(i);
  }
  var key = Aes.cipher(pwBytes, Aes.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<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i);
  
  // generate key schedule
  var keySchedule = Aes.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<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<nBlocks; b++) {
    // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
    for (var c=0; c<4; c++) counterBlock[15-c] = ((b) >>> c*8) & 0xff;
    for (var c=0; c<4; c++) counterBlock[15-c-4] = (((b+1)/0x100000000-1) >>> c*8) & 0xff;

    var cipherCntr = Aes.cipher(counterBlock, keySchedule);  // encrypt counter block

    var plaintxtByte = new Array(ciphertext[b].length);
    for (var i=0; i<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 = Utf8.decode(plaintext);  // decode from UTF8 back to Unicode multi-byte chars
  
  //alert((new Date()) - t);
  return plaintext;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/*  Base64 class: Base 64 encoding / decoding (c) Chris Veness 2002-2011                          */
/*    note: depends on Utf8 class                                                                 */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

var Base64 = {};  // Base64 namespace

Base64.code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

/**
 * 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 {String} str The string to be encoded as base-64
 * @param {Boolean} [utf8encode=false] Flag to indicate whether str is Unicode string to be encoded 
 *   to UTF8 before conversion to base64; otherwise string is assumed to be 8-bit characters
 * @returns {String} Base64-encoded string
 */ 
Base64.encode = function(str, 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;
  var b64 = Base64.code;
   
  plain = utf8encode? str.encodeUTF8(): str;
  
  c = plain.length % 3;  // pad string to length of multiple of 3
  if (c > 0) { while (c++ < 3) { pad+= '='; plain+= '\0'; } }
  // note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars
   
  for (c=0; c<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<<16 | o2<<8 | o3;
      
    h1 = bits>>18 & 0x3f;
    h2 = bits>>12 & 0x3f;
    h3 = bits>>6 & 0x3f;
    h4 = bits & 0x3f;

    // use hextets to index into code 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 in IE
  
  // 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 {String} str The string to be decoded from base-64
 * @param {Boolean} [utf8decode=false] Flag to indicate whether str is Unicode string to be decoded 
 *   from UTF8 after conversion from base64
 * @returns {String} decoded string
 */ 
Base64.decode = function(str, utf8decode) {
  utf8decode =  (typeof utf8decode == 'undefined')? false: utf8decode;
  var o1, o2, o3, h1, h2, h3, h4, bits, d=[], plain, coded;
  var b64 = Base64.code;

  coded = utf8decode? str.decodeUTF8(): str;
  
  
  for (var c=0; c<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<<18 | h2<<12 | h3<<6 | h4;
      
    o1 = bits>>>16 & 0xff;
    o2 = bits>>>8 & 0xff;
    o3 = bits & 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 in IE
   
  return utf8decode? plain.decodeUTF8(): plain; 
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/*  Utf8 class: encode / decode between multi-byte Unicode characters and UTF-8 multiple          */
/*              single-byte character encoding (c) Chris Veness 2002-2011                         */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

var Utf8 = {};  // Utf8 namespace

/**
 * Encode multi-byte Unicode string into utf-8 multiple single-byte characters 
 * (BMP / basic multilingual plane only)
 *
 * Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars
 *
 * @param {String} strUni Unicode string to be encoded as UTF-8
 * @returns {String} encoded string
 */
Utf8.encode = function(strUni) {
  // use regular expressions & String.replace callback function for better efficiency 
  // than procedural approaches
  var strUtf = strUni.replace(
      /[\u0080-\u07ff]/g,  // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz
      function(c) { 
        var cc = c.charCodeAt(0);
        return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); }
   );
  strUtf = strUtf.replace(
      /[\u0800-\uffff]/g,  // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz
      function(c) { 
        var cc = c.charCodeAt(0); 
        return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); }
   );
  return strUtf;
}

/**
 * Decode utf-8 encoded string back into multi-byte Unicode characters
 *
 * @param {String} strUtf UTF-8 string to be decoded back to Unicode
 * @returns {String} decoded string
 */
Utf8.decode = function(strUtf) {
  // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char!
  var strUni = strUtf.replace(
      /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g,  // 3-byte chars
      function(c) {  // (note parentheses for precence)
        var cc = ((c.charCodeAt(0)&0x0f)<<12) | ((c.charCodeAt(1)&0x3f)<<6) | (c.charCodeAt(2)&0x3f); 
        return String.fromCharCode(cc); }
   );
  strUni = strUni.replace(
      /[\u00c0-\u00df][\u0080-\u00bf]/g,                 // 2-byte chars
      function(c) {  // (note parentheses for precence)
        var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f;
        return String.fromCharCode(cc); }
   );
  return strUni;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
Published (Last edited): 15-12-2011