[Pkg-javascript-commits] [pdf.js] 83/207: Added support for decrypting PDF 1.7/2.0 Algorithm 5 revision 5 and 6. *Added AES128 Encryption *Added AES258 Encryption/Decryption *Added SHA256 *Added SHA512 *Added class to handle 8 byte integers and associated bit operations *Added SHA384 *Added routines to handle new algorithm and perform PDF2.0 hashing.

David Prévot taffit at moszumanska.debian.org
Mon Jul 28 15:36:33 UTC 2014


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository pdf.js.

commit 1fce2856c6c81163b9fbd701b7bb047b6df40573
Author: Daniel West <daniel.j.west at gmail.com>
Date:   Sat Apr 19 02:21:32 2014 -0400

    Added support for decrypting PDF 1.7/2.0 Algorithm 5 revision 5 and 6.
    *Added AES128 Encryption
    *Added AES258 Encryption/Decryption
    *Added SHA256
    *Added SHA512
    *Added class to handle 8 byte integers and associated bit operations
    *Added SHA384
    *Added routines to handle new algorithm and perform PDF2.0 hashing.
---
 src/core/crypto.js       | 1386 +++++++++++++++++++++++++++++++++++++++++++---
 test/unit/crypto_spec.js |  431 +++++++++++++-
 2 files changed, 1745 insertions(+), 72 deletions(-)

diff --git a/src/core/crypto.js b/src/core/crypto.js
index eb5d534..150a22d 100644
--- a/src/core/crypto.js
+++ b/src/core/crypto.js
@@ -103,13 +103,11 @@ var calculateMD5 = (function calculateMD5Closure() {
     padded[i++] = 0;
     padded[i++] = 0;
     padded[i++] = 0;
-    // chunking
-    // TODO ArrayBuffer ?
     var w = new Int32Array(16);
     for (i = 0; i < paddedLength;) {
       for (j = 0; j < 16; ++j, i += 4) {
         w[j] = (padded[i] | (padded[i + 1] << 8) |
-                (padded[i + 2] << 16) | (padded[i + 3] << 24));
+               (padded[i + 2] << 16) | (padded[i + 3] << 24));
       }
       var a = h0, b = h1, c = h2, d = h3, f, g;
       for (j = 0; j < 64; ++j) {
@@ -144,22 +142,839 @@ var calculateMD5 = (function calculateMD5Closure() {
       h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >>> 24) & 0xFF
     ]);
   }
+
   return hash;
 })();
+var Word64 = (function Word64Closure() {
+  function Word64(highInteger, lowInteger) {
+    this.low = lowInteger >>> 0;
+    this.high = highInteger >>> 0;
+  }
+  Word64.prototype = {
+    and: function Word64_and(word) {
+      return new Word64(this.high & word.high,
+                        this.low & word.low);
+    },
+    xor: function Word64_xor(word) {
+      return new Word64(this.high ^ word.high,
+                        this.low ^ word.low);
+    },
+
+    or: function Word64_or(word) {
+      return new Word64(this.high | word.high,
+                        this.low | word.low);
+    },
+
+    shiftRight: function Word64_shiftRight(places) {
+      if (places >= 32) {
+        return new Word64(0x00000000, this.high >>> (places - 32));
+      }
+      return new Word64(this.high >>> places,
+                        (this.low >>> places) | (this.high << 32 - places));
+    },
+
+    shiftLeft: function Word64_shiftLeft(places) {
+      if (places >= 32) {
+        return new Word64(this.low << (places - 32), 0x00000000);
+      }
+      return new Word64((this.high << places) |
+                        (this.low >>> 32 - places),
+                        (this.low << places));
+    },
+    not: function Word64_not() {
+      return new Word64(~this.high, ~this.low);
+    },
+    plus: function Word64_plus(word) {
+      var lowAdd = this.low + word.low;
+      var highAdd = this.high + word.high;
+      if (lowAdd > 0xFFFFFFFF) {
+        highAdd += 1;
+      }
+      return new Word64((highAdd | 0) >>> 0,
+                        (lowAdd | 0) >>> 0);
+    },
+    copyTo: function Word64_copyTo(bytes, offset) {
+      if (offset + bytes >= bytes.length) {
+        error('insufficient byte array length');
+      }
+      bytes[offset] = (this.high >>> 24) & 0xFF;
+      bytes[offset + 1] = (this.high >>> 16) & 0xFF;
+      bytes[offset + 2] = (this.high >>> 8) & 0xFF;
+      bytes[offset + 3] = (this.high) & 0xFF;
+      bytes[offset + 4] = (this.low >>> 24) & 0xFF;
+      bytes[offset + 5] = (this.low >>> 16) & 0xFF;
+      bytes[offset + 6] = (this.low >>> 8) & 0xFF;
+      bytes[offset + 7] = (this.low) & 0xFF;
+    }
+  };
+  return Word64;
+})();
+
+var calculateSHA256 = (function calculateSHA256Closure() {
+  function rotr(x, n) {
+    return (x >>> n) | (x << 32 - n);
+  }
+
+  function ch(x, y, z) {
+    return (x & y) ^ (~x & z);
+  }
+
+  function maj(x, y, z) {
+    return (x & y) ^ (x & z) ^ (y & z);
+  }
+
+  function sigma(x) {
+    return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
+  }
+
+  function sigmaPrime(x) {
+    return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
+  }
+
+  function littleSigma(x) {
+    return rotr(x, 7) ^ rotr(x, 18) ^ x >>> 3;
+  }
+
+  function littleSigmaPrime(x) {
+    return rotr(x, 17) ^ rotr(x, 19) ^ x >>> 10;
+  }
+
+  var k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+           0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+           0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+           0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+           0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+           0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+           0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+           0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+           0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+           0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+           0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+           0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+           0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+           0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+           0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+           0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
+
+  function hash(data, offset, length) {
+    // initial hash values
+    var h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372,
+        h3 = 0xa54ff53a, h4 = 0x510e527f, h5 = 0x9b05688c,
+        h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
+    // pre-processing
+    var paddedLength = Math.ceil((length + 9) / 64) * 64;
+    var padded = new Uint8Array(paddedLength);
+    var i, j, n;
+    for (i = 0; i < length; ++i) {
+      padded[i] = data[offset++];
+    }
+    padded[i++] = 0x80;
+    n = paddedLength - 8;
+    while (i < n) {
+      padded[i++] = 0;
+    }
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = (length >>> 29) & 0xFF;
+    padded[i++] = (length >> 21) & 0xFF;
+    padded[i++] = (length >> 13) & 0xFF;
+    padded[i++] = (length >> 5) & 0xFF;
+    padded[i++] = (length << 3) & 0xFF;
+    var w = new Uint32Array(64);
+    //for each 512 bit block
+    for (i = 0; i < paddedLength;) {
+      for (j = 0; j < 64; ++j) {
+        if (j < 16) {
+          w[j] = (padded[i] << 24 | (padded[i + 1] << 16) |
+                 (padded[i + 2] << 8) | (padded[i + 3]));
+          i += 4;
+        } else {
+          w[j] = littleSigmaPrime(w[j - 2]) + w[j - 7] +
+            littleSigma(w[j - 15]) + w[j - 16] | 0;
+        }
+      }
+      var a = h0, b = h1, c = h2, d = h3, e = h4,
+          f = h5, g = h6, h = h7, t1, t2;
+      for (j = 0; j < 64; ++j) {
+        t1 = h + sigmaPrime(e) + ch(e, f, g) + k[j] + w[j];
+        t2 = sigma(a) + maj(a, b, c);
+        h = g;
+        g = f;
+        f = e;
+        e = (d + t1) | 0;
+        d = c;
+        c = b;
+        b = a;
+        a = (t1 + t2) | 0;
+      }
+      h0 = (h0 + a) | 0;
+      h1 = (h1 + b) | 0;
+      h2 = (h2 + c) | 0;
+      h3 = (h3 + d) | 0;
+      h4 = (h4 + e) | 0;
+      h5 = (h5 + f) | 0;
+      h6 = (h6 + g) | 0;
+      h7 = (h7 + h) | 0;
+    }
+    return new Uint8Array([
+      (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, (h0) & 0xFF,
+      (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, (h1) & 0xFF,
+      (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, (h2) & 0xFF,
+      (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, (h3) & 0xFF,
+      (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, (h4) & 0xFF,
+      (h5 >> 24) & 0xFF, (h5 >> 16) & 0xFF, (h5 >> 8) & 0xFF, (h5) & 0xFF,
+      (h6 >> 24) & 0xFF, (h6 >> 16) & 0xFF, (h6 >> 8) & 0xFF, (h6) & 0xFF,
+      (h7 >> 24) & 0xFF, (h7 >> 16) & 0xFF, (h7 >> 8) & 0xFF, (h7) & 0xFF
+    ]);
+  }
+
+  return hash;
+})();
+
+var calculateSHA512 = (function calculateSHA512Closure() {
+  function rotr(x, n) {
+    return (x.shiftRight(n)).or(x.shiftLeft(64 - n));
+  }
+
+  function ch(x, y, z) {
+    return (x.and(y)).xor(x.not().and(z));
+  }
+
+  function maj(x, y, z) {
+    return (x.and(y)).xor(x.and(z)).xor(y.and(z));
+  }
+
+  function sigma(x) {
+    return rotr(x, 28).xor(rotr(x, 34)).xor(rotr(x, 39));
+  }
+
+  function sigmaPrime(x) {
+    return rotr(x, 14).xor(rotr(x, 18)).xor(rotr(x, 41));
+  }
+
+  function littleSigma(x) {
+    return rotr(x, 1).xor(rotr(x, 8)).xor(x.shiftRight(7));
+  }
+
+  function littleSigmaPrime(x) {
+    return rotr(x, 19).xor(rotr(x, 61)).xor(x.shiftRight(6));
+  }
+
+  var k = [
+    new Word64(0x428a2f98, 0xd728ae22), new Word64(0x71374491, 0x23ef65cd),
+    new Word64(0xb5c0fbcf, 0xec4d3b2f), new Word64(0xe9b5dba5, 0x8189dbbc),
+    new Word64(0x3956c25b, 0xf348b538), new Word64(0x59f111f1, 0xb605d019),
+    new Word64(0x923f82a4, 0xaf194f9b), new Word64(0xab1c5ed5, 0xda6d8118),
+    new Word64(0xd807aa98, 0xa3030242), new Word64(0x12835b01, 0x45706fbe),
+    new Word64(0x243185be, 0x4ee4b28c), new Word64(0x550c7dc3, 0xd5ffb4e2),
+    new Word64(0x72be5d74, 0xf27b896f), new Word64(0x80deb1fe, 0x3b1696b1),
+    new Word64(0x9bdc06a7, 0x25c71235), new Word64(0xc19bf174, 0xcf692694),
+    new Word64(0xe49b69c1, 0x9ef14ad2), new Word64(0xefbe4786, 0x384f25e3),
+    new Word64(0x0fc19dc6, 0x8b8cd5b5), new Word64(0x240ca1cc, 0x77ac9c65),
+    new Word64(0x2de92c6f, 0x592b0275), new Word64(0x4a7484aa, 0x6ea6e483),
+    new Word64(0x5cb0a9dc, 0xbd41fbd4), new Word64(0x76f988da, 0x831153b5),
+    new Word64(0x983e5152, 0xee66dfab), new Word64(0xa831c66d, 0x2db43210),
+    new Word64(0xb00327c8, 0x98fb213f), new Word64(0xbf597fc7, 0xbeef0ee4),
+    new Word64(0xc6e00bf3, 0x3da88fc2), new Word64(0xd5a79147, 0x930aa725),
+    new Word64(0x06ca6351, 0xe003826f), new Word64(0x14292967, 0x0a0e6e70),
+    new Word64(0x27b70a85, 0x46d22ffc), new Word64(0x2e1b2138, 0x5c26c926),
+    new Word64(0x4d2c6dfc, 0x5ac42aed), new Word64(0x53380d13, 0x9d95b3df),
+    new Word64(0x650a7354, 0x8baf63de), new Word64(0x766a0abb, 0x3c77b2a8),
+    new Word64(0x81c2c92e, 0x47edaee6), new Word64(0x92722c85, 0x1482353b),
+    new Word64(0xa2bfe8a1, 0x4cf10364), new Word64(0xa81a664b, 0xbc423001),
+    new Word64(0xc24b8b70, 0xd0f89791), new Word64(0xc76c51a3, 0x0654be30),
+    new Word64(0xd192e819, 0xd6ef5218), new Word64(0xd6990624, 0x5565a910),
+    new Word64(0xf40e3585, 0x5771202a), new Word64(0x106aa070, 0x32bbd1b8),
+    new Word64(0x19a4c116, 0xb8d2d0c8), new Word64(0x1e376c08, 0x5141ab53),
+    new Word64(0x2748774c, 0xdf8eeb99), new Word64(0x34b0bcb5, 0xe19b48a8),
+    new Word64(0x391c0cb3, 0xc5c95a63), new Word64(0x4ed8aa4a, 0xe3418acb),
+    new Word64(0x5b9cca4f, 0x7763e373), new Word64(0x682e6ff3, 0xd6b2b8a3),
+    new Word64(0x748f82ee, 0x5defb2fc), new Word64(0x78a5636f, 0x43172f60),
+    new Word64(0x84c87814, 0xa1f0ab72), new Word64(0x8cc70208, 0x1a6439ec),
+    new Word64(0x90befffa, 0x23631e28), new Word64(0xa4506ceb, 0xde82bde9),
+    new Word64(0xbef9a3f7, 0xb2c67915), new Word64(0xc67178f2, 0xe372532b),
+    new Word64(0xca273ece, 0xea26619c), new Word64(0xd186b8c7, 0x21c0c207),
+    new Word64(0xeada7dd6, 0xcde0eb1e), new Word64(0xf57d4f7f, 0xee6ed178),
+    new Word64(0x06f067aa, 0x72176fba), new Word64(0x0a637dc5, 0xa2c898a6),
+    new Word64(0x113f9804, 0xbef90dae), new Word64(0x1b710b35, 0x131c471b),
+    new Word64(0x28db77f5, 0x23047d84), new Word64(0x32caab7b, 0x40c72493),
+    new Word64(0x3c9ebe0a, 0x15c9bebc), new Word64(0x431d67c4, 0x9c100d4c),
+    new Word64(0x4cc5d4be, 0xcb3e42b6), new Word64(0x597f299c, 0xfc657e2a),
+    new Word64(0x5fcb6fab, 0x3ad6faec), new Word64(0x6c44198c, 0x4a475817)];
+
+  function hash(data, offset, length, mode384) {
+    mode384 = !!mode384;
+    // initial hash values
+    var h0, h1, h2, h3, h4, h5, h6, h7;
+    if (!mode384) {
+      h0 = new Word64(0x6a09e667, 0xf3bcc908);
+      h1 = new Word64(0xbb67ae85, 0x84caa73b);
+      h2 = new Word64(0x3c6ef372, 0xfe94f82b);
+      h3 = new Word64(0xa54ff53a, 0x5f1d36f1);
+      h4 = new Word64(0x510e527f, 0xade682d1);
+      h5 = new Word64(0x9b05688c, 0x2b3e6c1f);
+      h6 = new Word64(0x1f83d9ab, 0xfb41bd6b);
+      h7 = new Word64(0x5be0cd19, 0x137e2179);
+    }
+    else {
+      //SHA384 is exactly the same
+      //except with different starting values and a trimmed result
+      h0 = new Word64(0xcbbb9d5d, 0xc1059ed8);
+      h1 = new Word64(0x629a292a, 0x367cd507);
+      h2 = new Word64(0x9159015a, 0x3070dd17);
+      h3 = new Word64(0x152fecd8, 0xf70e5939);
+      h4 = new Word64(0x67332667, 0xffc00b31);
+      h5 = new Word64(0x8eb44a87, 0x68581511);
+      h6 = new Word64(0xdb0c2e0d, 0x64f98fa7);
+      h7 = new Word64(0x47b5481d, 0xbefa4fa4);
+    }
+
+    // pre-processing
+    var paddedLength = Math.ceil((length + 17) / 128) * 128;
+    var padded = new Uint8Array(paddedLength);
+    var i, j, n;
+    for (i = 0; i < length; ++i) {
+      padded[i] = data[offset++];
+    }
+    padded[i++] = 0x80;
+    n = paddedLength - 16;
+    while (i < n) {
+      padded[i++] = 0;
+    }
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = 0;
+    padded[i++] = (length >>> 29) & 0xFF;
+    padded[i++] = (length >> 21) & 0xFF;
+    padded[i++] = (length >> 13) & 0xFF;
+    padded[i++] = (length >> 5) & 0xFF;
+    padded[i++] = (length << 3) & 0xFF;
+
+    var w = new Array(128);
+    //for each 1024 bit block
+    for (i = 0; i < paddedLength;) {
+      for (j = 0; j < 80; ++j) {
+        if (j < 16) {
+          var value = new Word64(padded[i] << 24 |(padded[i + 1] << 16) |
+                                 (padded[i + 2] << 8) | (padded[i + 3]),
+                                 (padded[i + 4]) << 24 | (padded[i + 5]) << 16 |
+                                 (padded[i + 6]) << 8 | (padded[i + 7]));
+          w[j] = value;
+          i += 8;
+        } else {
+          w[j] = littleSigmaPrime(w[j - 2])
+            .plus(w[j - 7])
+            .plus(littleSigma(w[j - 15]))
+            .plus(w[j - 16]);
+        }
+
+      }
+      var a = h0, b = h1, c = h2, d = h3,
+          e = h4, f = h5, g = h6, h = h7,
+          t1, t2;
+      for (j = 0; j < 80; ++j) {
+        t1 = h.plus(sigmaPrime(e))
+          .plus(ch(e, f, g))
+          .plus(k[j])
+          .plus(w[j]);
+        t2 = sigma(a).plus(maj(a, b, c));
+        h = g;
+        g = f;
+        f = e;
+        e = (d.plus(t1));
+        d = c;
+        c = b;
+        b = a;
+        a = (t1.plus(t2));
+      }
+      h0 = (h0.plus(a));
+      h1 = (h1.plus(b));
+      h2 = (h2.plus(c));
+      h3 = (h3.plus(d));
+      h4 = (h4.plus(e));
+      h5 = (h5.plus(f));
+      h6 = (h6.plus(g));
+      h7 = (h7.plus(h));
+    }
+
+    var result;
+    if (!mode384) {
+      result = new Uint8Array(64);
+      h0.copyTo(result,0);
+      h1.copyTo(result,8);
+      h2.copyTo(result,16);
+      h3.copyTo(result,24);
+      h4.copyTo(result,32);
+      h5.copyTo(result,40);
+      h6.copyTo(result,48);
+      h7.copyTo(result,56);
+    }
+    else {
+      result = new Uint8Array(48);
+      h0.copyTo(result,0);
+      h1.copyTo(result,8);
+      h2.copyTo(result,16);
+      h3.copyTo(result,24);
+      h4.copyTo(result,32);
+      h5.copyTo(result,40);
+    }
+    return result;
+  }
+
+  return hash;
+})();
+var calculateSHA384 = (function calculateSHA384Closure() {
+  function hash(data, offset, length) {
+    return calculateSHA512(data, offset, length, true);
+  }
+
+  return hash;
+})();
+var NullCipher = (function NullCipherClosure() {
+  function NullCipher() {
+  }
+
+  NullCipher.prototype = {
+    decryptBlock: function NullCipher_decryptBlock(data) {
+      return data;
+    }
+  };
+
+  return NullCipher;
+})();
+
+var AES128Cipher = (function AES128CipherClosure() {
+  var rcon = new Uint8Array([
+    0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
+    0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
+    0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
+    0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
+    0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+    0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6,
+    0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72,
+    0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
+    0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10,
+    0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e,
+    0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,
+    0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
+    0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02,
+    0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,
+    0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,
+    0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
+    0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb,
+    0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
+    0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
+    0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
+    0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
+    0x74, 0xe8, 0xcb, 0x8d]);
+
+  var s = new Uint8Array([
+    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]);
+
+  var inv_s = new Uint8Array([
+    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
+    0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+    0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
+    0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
+    0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+    0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
+    0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
+    0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+    0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
+    0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
+    0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+    0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
+    0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
+    0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+    0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
+    0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
+    0x55, 0x21, 0x0c, 0x7d]);
+  var mixCol = new Uint8Array(256);
+  for (var i = 0; i < 256; i++) {
+    if (i < 128) {
+      mixCol[i] = i << 1;
+    } else {
+      mixCol[i] = (i << 1) ^ 0x1b;
+    }
+  }
+  var mix = new Uint32Array([
+    0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927,
+    0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45,
+    0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb,
+    0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381,
+    0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf,
+    0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66,
+    0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28,
+    0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012,
+    0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec,
+    0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e,
+    0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd,
+    0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7,
+    0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89,
+    0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b,
+    0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815,
+    0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f,
+    0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa,
+    0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8,
+    0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36,
+    0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c,
+    0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742,
+    0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea,
+    0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4,
+    0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e,
+    0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360,
+    0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502,
+    0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87,
+    0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd,
+    0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3,
+    0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621,
+    0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f,
+    0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55,
+    0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26,
+    0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844,
+    0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba,
+    0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480,
+    0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce,
+    0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67,
+    0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929,
+    0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713,
+    0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed,
+    0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f,
+    0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]);
+
+  function expandKey128(cipherKey) {
+    var b = 176, result = new Uint8Array(b);
+    result.set(cipherKey);
+    for (var j = 16, i = 1; j < b; ++i) {
+      // RotWord
+      var t1 = result[j - 3], t2 = result[j - 2],
+          t3 = result[j - 1], t4 = result[j - 4];
+      // SubWord
+      t1 = s[t1];
+      t2 = s[t2];
+      t3 = s[t3];
+      t4 = s[t4];
+      // Rcon
+      t1 = t1 ^ rcon[i];
+      for (var n = 0; n < 4; ++n) {
+        result[j] = (t1 ^= result[j - 16]);
+        j++;
+        result[j] = (t2 ^= result[j - 16]);
+        j++;
+        result[j] = (t3 ^= result[j - 16]);
+        j++;
+        result[j] = (t4 ^= result[j - 16]);
+        j++;
+      }
+    }
+    return result;
+  }
+
+  function decrypt128(input, key) {
+    var state = new Uint8Array(16);
+    state.set(input);
+    var i, j, k;
+    var t, u, v;
+    // AddRoundKey
+    for (j = 0, k = 160; j < 16; ++j, ++k) {
+      state[j] ^= key[k];
+    }
+    for (i = 9; i >= 1; --i) {
+      // InvShiftRows
+      t = state[13];
+      state[13] = state[9];
+      state[9] = state[5];
+      state[5] = state[1];
+      state[1] = t;
+      t = state[14];
+      u = state[10];
+      state[14] = state[6];
+      state[10] = state[2];
+      state[6] = t;
+      state[2] = u;
+      t = state[15];
+      u = state[11];
+      v = state[7];
+      state[15] = state[3];
+      state[11] = t;
+      state[7] = u;
+      state[3] = v;
+      // InvSubBytes
+      for (j = 0; j < 16; ++j) {
+        state[j] = inv_s[state[j]];
+      }
+      // AddRoundKey
+      for (j = 0, k = i * 16; j < 16; ++j, ++k) {
+        state[j] ^= key[k];
+      }
+      // InvMixColumns
+      for (j = 0; j < 16; j += 4) {
+        var s0 = mix[state[j]], s1 = mix[state[j + 1]],
+          s2 = mix[state[j + 2]], s3 = mix[state[j + 3]];
+        t = (s0 ^ (s1 >>> 8) ^ (s1 << 24) ^ (s2 >>> 16) ^ (s2 << 16) ^
+          (s3 >>> 24) ^ (s3 << 8));
+        state[j] = (t >>> 24) & 0xFF;
+        state[j + 1] = (t >> 16) & 0xFF;
+        state[j + 2] = (t >> 8) & 0xFF;
+        state[j + 3] = t & 0xFF;
+      }
+    }
+    // InvShiftRows
+    t = state[13];
+    state[13] = state[9];
+    state[9] = state[5];
+    state[5] = state[1];
+    state[1] = t;
+    t = state[14];
+    u = state[10];
+    state[14] = state[6];
+    state[10] = state[2];
+    state[6] = t;
+    state[2] = u;
+    t = state[15];
+    u = state[11];
+    v = state[7];
+    state[15] = state[3];
+    state[11] = t;
+    state[7] = u;
+    state[3] = v;
+    for (j = 0; j < 16; ++j) {
+      // InvSubBytes
+      state[j] = inv_s[state[j]];
+      // AddRoundKey
+      state[j] ^= key[j];
+    }
+    return state;
+  }
 
-var NullCipher = (function NullCipherClosure() {
-  function NullCipher() {}
+  function encrypt128(input, key) {
+    var t, u, v, k;
+    var state = new Uint8Array(16);
+    state.set(input);
+    for (j = 0; j < 16; ++j) {
+      // AddRoundKey
+      state[j] ^= key[j];
+    }
 
-  NullCipher.prototype = {
-    decryptBlock: function NullCipher_decryptBlock(data) {
-      return data;
+    for (i = 1; i < 10; i++) {
+      //SubBytes
+      for (j = 0; j < 16; ++j) {
+        state[j] = s[state[j]];
+      }
+      //ShiftRows
+      v = state[1];
+      state[1] = state[5];
+      state[5] = state[9];
+      state[9] = state[13];
+      state[13] = v;
+      v = state[2];
+      u = state[6];
+      state[2] = state[10];
+      state[6] = state[14];
+      state[10] = v;
+      state[14] = u;
+      v = state[3];
+      u = state[7];
+      t = state[11];
+      state[3] = state[15];
+      state[7] = v;
+      state[11] = u;
+      state[15] = t;
+      //MixColumns
+      for (var j = 0; j < 16; j += 4) {
+        var s0 = state[j + 0], s1 = state[j + 1];
+        var s2 = state[j + 2], s3 = state[j + 3];
+        t = s0 ^ s1 ^ s2 ^ s3;
+        state[j + 0] ^= t ^ mixCol[s0 ^ s1];
+        state[j + 1] ^= t ^ mixCol[s1 ^ s2];
+        state[j + 2] ^= t ^ mixCol[s2 ^ s3];
+        state[j + 3] ^= t ^ mixCol[s3 ^ s0];
+      }
+      //AddRoundKey
+      for (j = 0, k = i * 16; j < 16; ++j, ++k) {
+        state[j] ^= key[k];
+      }
+    }
+
+    //SubBytes
+    for (j = 0; j < 16; ++j) {
+      state[j] = s[state[j]];
+    }
+    //ShiftRows
+    v = state[1];
+    state[1] = state[5];
+    state[5] = state[9];
+    state[9] = state[13];
+    state[13] = v;
+    v = state[2];
+    u = state[6];
+    state[2] = state[10];
+    state[6] = state[14];
+    state[10] = v;
+    state[14] = u;
+    v = state[3];
+    u = state[7];
+    t = state[11];
+    state[3] = state[15];
+    state[7] = v;
+    state[11] = u;
+    state[15] = t;
+    //AddRoundKey
+    for (j = 0, k = 160; j < 16; ++j, ++k) {
+      state[j] ^= key[k];
+    }
+    return state;
+  }
+
+  function AES128Cipher(key) {
+    this.key = expandKey128(key);
+    this.buffer = new Uint8Array(16);
+    this.bufferPosition = 0;
+  }
+
+  function decryptBlock2(data, finalize) {
+    var i, j, ii, sourceLength = data.length,
+        buffer = this.buffer, bufferLength = this.bufferPosition,
+        result = [], iv = this.iv;
+    for (i = 0; i < sourceLength; ++i) {
+      buffer[bufferLength] = data[i];
+      ++bufferLength;
+      if (bufferLength < 16) {
+        continue;
+      }
+      // buffer is full, decrypting
+      var plain = decrypt128(buffer, this.key);
+      // xor-ing the IV vector to get plain text
+      for (j = 0; j < 16; ++j) {
+        plain[j] ^= iv[j];
+      }
+      iv = buffer;
+      result.push(plain);
+      buffer = new Uint8Array(16);
+      bufferLength = 0;
+    }
+    // saving incomplete buffer
+    this.buffer = buffer;
+    this.bufferLength = bufferLength;
+    this.iv = iv;
+    if (result.length === 0) {
+      return new Uint8Array([]);
+    }
+    // combining plain text blocks into one
+    var outputLength = 16 * result.length;
+    if (finalize) {
+      // undo a padding that is described in RFC 2898
+      var lastBlock = result[result.length - 1];
+      outputLength -= lastBlock[15];
+      result[result.length - 1] = lastBlock.subarray(0, 16 - lastBlock[15]);
+    }
+    var output = new Uint8Array(outputLength);
+    for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {
+      output.set(result[i], j);
+    }
+    return output;
+  }
+
+  AES128Cipher.prototype = {
+    decryptBlock: function AES128Cipher_decryptBlock(data, finalize) {
+      var i, sourceLength = data.length;
+      var buffer = this.buffer, bufferLength = this.bufferPosition;
+      // waiting for IV values -- they are at the start of the stream
+      for (i = 0; bufferLength < 16 && i < sourceLength; ++i, ++bufferLength) {
+        buffer[bufferLength] = data[i];
+      }
+      if (bufferLength < 16) {
+        // need more data
+        this.bufferLength = bufferLength;
+        return new Uint8Array([]);
+      }
+      this.iv = buffer;
+      this.buffer = new Uint8Array(16);
+      this.bufferLength = 0;
+      // starting decryption
+      this.decryptBlock = decryptBlock2;
+      return this.decryptBlock(data.subarray(16), finalize);
+    },
+    encrypt: function AES128Cipher_encrypt(data, iv) {
+      var i, j, ii, sourceLength = data.length,
+          buffer = this.buffer, bufferLength = this.bufferPosition,
+          result = [];
+      if (!iv) {
+        iv = new Uint8Array(16);
+      }
+      for (i = 0; i < sourceLength; ++i) {
+        buffer[bufferLength] = data[i];
+        ++bufferLength;
+        if (bufferLength < 16) {
+          continue;
+        }
+        for (j = 0; j < 16; ++j) {
+          buffer[j] ^= iv[j];
+        }
+
+        // buffer is full, encrypting
+        var cipher = encrypt128(buffer, this.key);
+        iv = cipher;
+        result.push(cipher);
+        buffer = new Uint8Array(16);
+        bufferLength = 0;
+      }
+      // saving incomplete buffer
+      this.buffer = buffer;
+      this.bufferLength = bufferLength;
+      this.iv = iv;
+      if (result.length === 0) {
+        return new Uint8Array([]);
+      }
+      // combining plain text blocks into one
+      var outputLength = 16 * result.length;
+      var output = new Uint8Array(outputLength);
+      for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {
+        output.set(result[i], j);
+      }
+      return output;
     }
   };
 
-  return NullCipher;
+  return AES128Cipher;
 })();
 
-var AES128Cipher = (function AES128CipherClosure() {
+var AES256Cipher = (function AES256CipherClosure() {
   var rcon = new Uint8Array([
     0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
     0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
@@ -232,6 +1047,14 @@ var AES128Cipher = (function AES128CipherClosure() {
     0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
     0x55, 0x21, 0x0c, 0x7d]);
 
+  var mixCol = new Uint8Array(256);
+  for (var i = 0; i < 256; i++) {
+    if (i < 128) {
+      mixCol[i] = i << 1;
+    } else {
+      mixCol[i] = (i << 1) ^ 0x1b;
+    }
+  }
   var mix = new Uint32Array([
     0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927,
     0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45,
@@ -277,44 +1100,76 @@ var AES128Cipher = (function AES128CipherClosure() {
     0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f,
     0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]);
 
-  function expandKey128(cipherKey) {
-    var b = 176, result = new Uint8Array(b);
+  function expandKey256(cipherKey) {
+    var b = 240, result = new Uint8Array(b);
+    var r = 1;
+
     result.set(cipherKey);
-    for (var j = 16, i = 1; j < b; ++i) {
-      // RotWord
-      var t1 = result[j - 3], t2 = result[j - 2],
+    for (var j = 32, i = 1; j < b; ++i) {
+      if (j % 32 === 16) {
+        t1 = s[t1];
+        t2 = s[t2];
+        t3 = s[t3];
+        t4 = s[t4];
+      } else if (j % 32 === 0) {
+        // RotWord
+        var t1 = result[j - 3], t2 = result[j - 2],
           t3 = result[j - 1], t4 = result[j - 4];
-      // SubWord
-      t1 = s[t1]; t2 = s[t2]; t3 = s[t3]; t4 = s[t4];
-      // Rcon
-      t1 = t1 ^ rcon[i];
+        // SubWord
+        t1 = s[t1];
+        t2 = s[t2];
+        t3 = s[t3];
+        t4 = s[t4];
+        // Rcon
+        t1 = t1 ^ r;
+        if ((r <<= 1) >= 256) {
+          r = (r ^ 0x1b) & 0xFF;
+        }
+      }
+
       for (var n = 0; n < 4; ++n) {
-        result[j] = (t1 ^= result[j - 16]); j++;
-        result[j] = (t2 ^= result[j - 16]); j++;
-        result[j] = (t3 ^= result[j - 16]); j++;
-        result[j] = (t4 ^= result[j - 16]); j++;
+        result[j] = (t1 ^= result[j - 32]);
+        j++;
+        result[j] = (t2 ^= result[j - 32]);
+        j++;
+        result[j] = (t3 ^= result[j - 32]);
+        j++;
+        result[j] = (t4 ^= result[j - 32]);
+        j++;
       }
     }
     return result;
   }
 
-  function decrypt128(input, key) {
+  function decrypt256(input, key) {
     var state = new Uint8Array(16);
     state.set(input);
     var i, j, k;
     var t, u, v;
     // AddRoundKey
-    for (j = 0, k = 160; j < 16; ++j, ++k) {
+    for (j = 0, k = 224; j < 16; ++j, ++k) {
       state[j] ^= key[k];
     }
-    for (i = 9; i >= 1; --i) {
+    for (i = 13; i >= 1; --i) {
       // InvShiftRows
-      t = state[13]; state[13] = state[9]; state[9] = state[5];
-      state[5] = state[1]; state[1] = t;
-      t = state[14]; u = state[10]; state[14] = state[6];
-      state[10] = state[2]; state[6] = t; state[2] = u;
-      t = state[15]; u = state[11]; v = state[7]; state[15] = state[3];
-      state[11] = t; state[7] = u; state[3] = v;
+      t = state[13];
+      state[13] = state[9];
+      state[9] = state[5];
+      state[5] = state[1];
+      state[1] = t;
+      t = state[14];
+      u = state[10];
+      state[14] = state[6];
+      state[10] = state[2];
+      state[6] = t;
+      state[2] = u;
+      t = state[15];
+      u = state[11];
+      v = state[7];
+      state[15] = state[3];
+      state[11] = t;
+      state[7] = u;
+      state[3] = v;
       // InvSubBytes
       for (j = 0; j < 16; ++j) {
         state[j] = inv_s[state[j]];
@@ -336,12 +1191,24 @@ var AES128Cipher = (function AES128CipherClosure() {
       }
     }
     // InvShiftRows
-    t = state[13]; state[13] = state[9]; state[9] = state[5];
-    state[5] = state[1]; state[1] = t;
-    t = state[14]; u = state[10]; state[14] = state[6];
-    state[10] = state[2]; state[6] = t; state[2] = u;
-    t = state[15]; u = state[11]; v = state[7]; state[15] = state[3];
-    state[11] = t; state[7] = u; state[3] = v;
+    t = state[13];
+    state[13] = state[9];
+    state[9] = state[5];
+    state[5] = state[1];
+    state[1] = t;
+    t = state[14];
+    u = state[10];
+    state[14] = state[6];
+    state[10] = state[2];
+    state[6] = t;
+    state[2] = u;
+    t = state[15];
+    u = state[11];
+    v = state[7];
+    state[15] = state[3];
+    state[11] = t;
+    state[7] = u;
+    state[3] = v;
     for (j = 0; j < 16; ++j) {
       // InvSubBytes
       state[j] = inv_s[state[j]];
@@ -351,8 +1218,89 @@ var AES128Cipher = (function AES128CipherClosure() {
     return state;
   }
 
-  function AES128Cipher(key) {
-    this.key = expandKey128(key);
+  function encrypt256(input, key) {
+    var t, u, v, k;
+    var state = new Uint8Array(16);
+    state.set(input);
+    for (j = 0; j < 16; ++j) {
+      // AddRoundKey
+      state[j] ^= key[j];
+    }
+
+    for (i = 1; i < 14; i++) {
+      //SubBytes
+      for (j = 0; j < 16; ++j) {
+        state[j] = s[state[j]];
+      }
+      //ShiftRows
+      v = state[1];
+      state[1] = state[5];
+      state[5] = state[9];
+      state[9] = state[13];
+      state[13] = v;
+      v = state[2];
+      u = state[6];
+      state[2] = state[10];
+      state[6] = state[14];
+      state[10] = v;
+      state[14] = u;
+      v = state[3];
+      u = state[7];
+      t = state[11];
+      state[3] = state[15];
+      state[7] = v;
+      state[11] = u;
+      state[15] = t;
+      //MixColumns
+      for (var j = 0; j < 16; j += 4) {
+        var s0 = state[j + 0], s1 = state[j + 1];
+        var s2 = state[j + 2], s3 = state[j + 3];
+        t = s0 ^ s1 ^ s2 ^ s3;
+        state[j + 0] ^= t ^ mixCol[s0 ^ s1];
+        state[j + 1] ^= t ^ mixCol[s1 ^ s2];
+        state[j + 2] ^= t ^ mixCol[s2 ^ s3];
+        state[j + 3] ^= t ^ mixCol[s3 ^ s0];
+      }
+      //AddRoundKey
+      for (j = 0, k = i * 16; j < 16; ++j, ++k) {
+        state[j] ^= key[k];
+      }
+    }
+
+    //SubBytes
+    for (j = 0; j < 16; ++j) {
+      state[j] = s[state[j]];
+    }
+    //ShiftRows
+    v = state[1];
+    state[1] = state[5];
+    state[5] = state[9];
+    state[9] = state[13];
+    state[13] = v;
+    v = state[2];
+    u = state[6];
+    state[2] = state[10];
+    state[6] = state[14];
+    state[10] = v;
+    state[14] = u;
+    v = state[3];
+    u = state[7];
+    t = state[11];
+    state[3] = state[15];
+    state[7] = v;
+    state[11] = u;
+    state[15] = t;
+    //AddRoundKey
+    for (j = 0, k = 224; j < 16; ++j, ++k) {
+      state[j] ^= key[k];
+    }
+
+    return state;
+
+  }
+
+  function AES256Cipher(key) {
+    this.key = expandKey256(key);
     this.buffer = new Uint8Array(16);
     this.bufferPosition = 0;
   }
@@ -361,14 +1309,15 @@ var AES128Cipher = (function AES128CipherClosure() {
     var i, j, ii, sourceLength = data.length,
         buffer = this.buffer, bufferLength = this.bufferPosition,
         result = [], iv = this.iv;
+
     for (i = 0; i < sourceLength; ++i) {
       buffer[bufferLength] = data[i];
       ++bufferLength;
       if (bufferLength < 16) {
         continue;
       }
-      // buffer is full, decrypting
-      var plain = decrypt128(buffer, this.key);
+      // buffer is full, encrypting
+      var plain = decrypt256(buffer, this.key);
       // xor-ing the IV vector to get plain text
       for (j = 0; j < 16; ++j) {
         plain[j] ^= iv[j];
@@ -398,31 +1347,267 @@ var AES128Cipher = (function AES128CipherClosure() {
       output.set(result[i], j);
     }
     return output;
+
   }
 
-  AES128Cipher.prototype = {
-    decryptBlock: function AES128Cipher_decryptBlock(data, finalize) {
+  AES256Cipher.prototype = {
+    decryptBlock: function AES256Cipher_decryptBlock(data, finalize, iv) {
       var i, sourceLength = data.length;
       var buffer = this.buffer, bufferLength = this.bufferPosition;
-      // waiting for IV values -- they are at the start of the stream
-      for (i = 0; bufferLength < 16 && i < sourceLength; ++i, ++bufferLength) {
-        buffer[bufferLength] = data[i];
-      }
-      if (bufferLength < 16) {
-        // need more data
-        this.bufferLength = bufferLength;
-        return new Uint8Array([]);
+      // if not supplied an IV wait for IV values
+      // they are at the start of the stream
+      if (iv) {
+        this.iv = iv;
+      } else {
+        for (i = 0; bufferLength < 16 &&
+             i < sourceLength; ++i, ++bufferLength) {
+          buffer[bufferLength] = data[i];
+        }
+        if (bufferLength < 16) {
+          //need more data
+          this.bufferLength = bufferLength;
+          return new Uint8Array([]);
+        }
+        this.iv = buffer;
+        data = data.subarray(16);
       }
-      this.iv = buffer;
       this.buffer = new Uint8Array(16);
       this.bufferLength = 0;
       // starting decryption
       this.decryptBlock = decryptBlock2;
-      return this.decryptBlock(data.subarray(16), finalize);
+      return this.decryptBlock(data, finalize);
+    },
+    encrypt: function AES256Cipher_encrypt(data, iv) {
+      var i, j, ii, sourceLength = data.length,
+          buffer = this.buffer, bufferLength = this.bufferPosition,
+          result = [];
+      if (!iv) {
+        iv = new Uint8Array(16);
+      }
+      for (i = 0; i < sourceLength; ++i) {
+        buffer[bufferLength] = data[i];
+        ++bufferLength;
+        if (bufferLength < 16) {
+          continue;
+        }
+        for (j = 0; j < 16; ++j) {
+          buffer[j] ^= iv[j];
+        }
+
+        // buffer is full, encrypting
+        var cipher = encrypt256(buffer, this.key);
+        this.iv = cipher;
+        result.push(cipher);
+        buffer = new Uint8Array(16);
+        bufferLength = 0;
+      }
+      // saving incomplete buffer
+      this.buffer = buffer;
+      this.bufferLength = bufferLength;
+      this.iv = iv;
+      if (result.length === 0) {
+        return new Uint8Array([]);
+      }
+      // combining plain text blocks into one
+      var outputLength = 16 * result.length;
+      var output = new Uint8Array(outputLength);
+      for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) {
+        output.set(result[i], j);
+      }
+      return output;
     }
   };
 
-  return AES128Cipher;
+  return AES256Cipher;
+})();
+
+var PDF17= (function PDF17Closure() {
+
+  function compareByteArrays(array1, array2) {
+    if (array1.length !== array2.length) {
+      return false;
+    }
+    for (var i = 0; i < array1.length; i++) {
+      if (array1[i] !== array2[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  function PDF17() {
+  }
+
+  PDF17.prototype = {
+    checkOwnerPassword: function PDF17_checkOwnerPassword(password,
+                                                          ownerValidationSalt,
+                                                          userBytes,
+                                                          ownerPassword) {
+      var hashData = new Uint8Array(password.length + 56);
+      hashData.set(password, 0);
+      hashData.set(ownerValidationSalt, password.length);
+      hashData.set(userBytes, password.length + ownerValidationSalt.length);
+      var result = calculateSHA256(hashData, 0, hashData.length);
+      return compareByteArrays(result, ownerPassword);
+    },
+    checkUserPassword: function PDF17_checkUserPassword(password,
+                                                        userValidationSalt,
+                                                        userPassword) {
+      var hashData = new Uint8Array(password.length + 8);
+      hashData.set(password, 0);
+      hashData.set(userValidationSalt, password.length);
+      var result = calculateSHA256(hashData, 0, hashData.length);
+      return compareByteArrays(result, userPassword);
+    },
+    getOwnerKey: function PDF17_getOwnerKey(password, ownerKeySalt, userBytes,
+                                            ownerEncryption) {
+      var hashData = new Uint8Array(password.length + 56);
+      hashData.set(password, 0);
+      hashData.set(ownerKeySalt, password.length);
+      hashData.set(userBytes, password.length + ownerKeySalt.length);
+      var key = calculateSHA256(hashData, 0, hashData.length);
+      var cipher = new AES256Cipher(key);
+      return cipher.decryptBlock(ownerEncryption,
+                                 false,
+                                 new Uint8Array(16));
+
+    },
+    getUserKey: function PDF17_getUserKey(password, userKeySalt,
+                                          userEncryption) {
+      var hashData = new Uint8Array(password.length + 8);
+      hashData.set(password, 0);
+      hashData.set(userKeySalt, password.length);
+      //key is the decryption key for the UE string
+      var key = calculateSHA256(hashData, 0, hashData.length);
+      var cipher = new AES256Cipher(key);
+      return cipher.decryptBlock(userEncryption,
+                                 false,
+                                 new Uint8Array(16));
+    }
+  };
+  return PDF17;
+})();
+
+var PDF20 = (function PDF20Closure() {
+
+  function concatArrays(array1, array2) {
+    var t = new Uint8Array(array1.length + array2.length);
+    t.set(array1, 0);
+    t.set(array2, array1.length);
+    return t;
+  }
+
+  function calculatePDF20Hash(password, input, userBytes) {
+    //This refers to Algorithm 2.B as defined in ISO 32000-2
+    var k = calculateSHA256(input, 0, input.length).subarray(0, 32);
+    var e = [0];
+    var i = 0;
+    while (i < 64 || e[e.length - 1] > i - 32) {
+      var arrayLength = password.length + k.length + userBytes.length;
+
+      var k1 = new Uint8Array(arrayLength * 64);
+      var array = concatArrays(password, k);
+      array = concatArrays(array, userBytes);
+      for (var j = 0, pos = 0; j < 64; j++, pos += arrayLength) {
+        k1.set(array, pos);
+      }
+      //AES128 CBC NO PADDING with
+      //first 16 bytes of k as the key and the second 16 as the iv.
+      var cipher = new AES128Cipher(k.subarray(0, 16));
+      e = cipher.encrypt(k1, k.subarray(16, 32));
+      //Now we have to take the first 16 bytes of an unsigned
+      //big endian integer... and compute the remainder
+      //modulo 3.... That is a fairly large number and
+      //JavaScript isn't going to handle that well...
+      //So we're using a trick that allows us to perform
+      //modulo math byte by byte
+      var remainder = 0;
+      for (var z = 0; z < 16; z++) {
+        remainder *= (256 % 3);
+        remainder %= 3;
+        remainder += ((e[z] >>> 0) % 3);
+        remainder %= 3;
+      }
+      if (remainder === 0) {
+        k = calculateSHA256(e, 0, e.length);
+      }
+      else if (remainder === 1) {
+        k = calculateSHA384(e, 0, e.length);
+      }
+      else if (remainder === 2) {
+        k = calculateSHA512(e, 0, e.length);
+      }
+      i++;
+    }
+    return k.subarray(0, 32);
+  }
+
+  function PDF20() {
+  }
+
+  function compareByteArrays(array1, array2) {
+    if (array1.length !== array2.length) {
+      return false;
+    }
+    for (var i = 0; i < array1.length; i++) {
+      if (array1[i] !== array2[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  PDF20.prototype = {
+    hash: function PDF20_hash(password, concatBytes, userBytes) {
+      return calculatePDF20Hash(password, concatBytes, userBytes);
+    },
+    checkOwnerPassword: function PDF20_checkOwnerPassword(password,
+                                                          ownerValidationSalt,
+                                                          userBytes,
+                                                          ownerPassword) {
+      var hashData = new Uint8Array(password.length + 56);
+      hashData.set(password, 0);
+      hashData.set(ownerValidationSalt, password.length);
+      hashData.set(userBytes, password.length + ownerValidationSalt.length);
+      var result = calculatePDF20Hash(password, hashData, userBytes);
+      return compareByteArrays(result, ownerPassword);
+    },
+    checkUserPassword: function PDF20_checkUserPassword(password,
+                                                        userValidationSalt,
+                                                        userPassword) {
+      var hashData = new Uint8Array(password.length + 8);
+      hashData.set(password, 0);
+      hashData.set(userValidationSalt, password.length);
+      var result = calculatePDF20Hash(password, hashData, []);
+      return compareByteArrays(result, userPassword);
+    },
+    getOwnerKey: function PDF20_getOwnerKey(password, ownerKeySalt, userBytes,
+                                            ownerEncryption) {
+      var hashData = new Uint8Array(password.length + 56);
+      hashData.set(password, 0);
+      hashData.set(ownerKeySalt, password.length);
+      hashData.set(userBytes, password.length + ownerKeySalt.length);
+      var key = calculatePDF20Hash(password, hashData, userBytes);
+      var cipher = new AES256Cipher(key);
+      return cipher.decryptBlock(ownerEncryption,
+                                 false,
+                                 new Uint8Array(16));
+
+    },
+    getUserKey: function PDF20_getUserKey(password, userKeySalt,
+                                          userEncryption) {
+      var hashData = new Uint8Array(password.length + 8);
+      hashData.set(password, 0);
+      hashData.set(userKeySalt, password.length);
+      //key is the decryption key for the UE string
+      var key = calculatePDF20Hash(password, hashData, []);
+      var cipher = new AES256Cipher(key);
+      return cipher.decryptBlock(userEncryption,
+                                 false,
+                                 new Uint8Array(16));
+    }
+  };
+  return PDF20;
 })();
 
 var CipherTransform = (function CipherTransformClosure() {
@@ -430,6 +1615,7 @@ var CipherTransform = (function CipherTransformClosure() {
     this.stringCipherConstructor = stringCipherConstructor;
     this.streamCipherConstructor = streamCipherConstructor;
   }
+
   CipherTransform.prototype = {
     createStream: function CipherTransform_createStream(stream, length) {
       var cipher = new this.streamCipherConstructor();
@@ -456,6 +1642,38 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
     0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
     0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
 
+  function createEncryptionKey20(revision, password, ownerPassword,
+                                 ownerValidationSalt, ownerKeySalt, uBytes,
+                                 userPassword, userValidationSalt, userKeySalt,
+                                 ownerEncryption, userEncryption, perms) {
+    if (password) {
+      var passwordLength = Math.min(127, password.length);
+      password = password.subarray(0, passwordLength);
+    } else {
+      password = [];
+    }
+    var pdfAlgorithm;
+    if (revision === 6) {
+      pdfAlgorithm = new PDF20();
+    } else {
+      pdfAlgorithm = new PDF17();
+    }
+
+    if (pdfAlgorithm) {
+      if (pdfAlgorithm.checkUserPassword(password, userValidationSalt,
+                                         userPassword)) {
+        return pdfAlgorithm.getUserKey(password, userKeySalt, userEncryption);
+      } else if (pdfAlgorithm.checkOwnerPassword(password, ownerValidationSalt,
+                                                 uBytes,
+                                                 ownerPassword)) {
+        return pdfAlgorithm.getOwnerKey(password, ownerKeySalt, uBytes,
+                                        ownerEncryption);
+      }
+    }
+
+    return null;
+  }
+
   function prepareKeyData(fileId, password, ownerPassword, userPassword,
                           flags, revision, keyLength, encryptMetadata) {
     var hashDataSize = 40 + ownerPassword.length + fileId.length;
@@ -516,7 +1734,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
         checkData = cipher.encryptBlock(checkData);
       }
       for (j = 0, n = checkData.length; j < n; ++j) {
-        if (userPassword[j] != checkData[j]) {
+        if (userPassword[j] !== checkData[j]) {
           return null;
         }
       }
@@ -524,7 +1742,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
       cipher = new ARCFourCipher(encryptionKey);
       checkData = cipher.encryptBlock(defaultPasswordBytes);
       for (j = 0, n = checkData.length; j < n; ++j) {
-        if (userPassword[j] != checkData[j]) {
+        if (userPassword[j] !== checkData[j]) {
           return null;
         }
       }
@@ -572,13 +1790,14 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
 
   function CipherTransformFactory(dict, fileId, password) {
     var filter = dict.get('Filter');
-    if (!isName(filter) || filter.name != 'Standard') {
+    if (!isName(filter) || filter.name !== 'Standard') {
       error('unknown encryption method');
     }
     this.dict = dict;
     var algorithm = dict.get('V');
     if (!isInt(algorithm) ||
-        (algorithm != 1 && algorithm != 2 && algorithm != 4)) {
+        (algorithm !== 1 && algorithm !== 2 && algorithm !== 4 &&
+        algorithm !== 5)) {
       error('unsupported encryption algorithm');
     }
     this.algorithm = algorithm;
@@ -593,8 +1812,9 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
     var userPassword = stringToBytes(dict.get('U')).subarray(0, 32);
     var flags = dict.get('P');
     var revision = dict.get('R');
-    var encryptMetadata = (algorithm == 4 &&  // meaningful when V is 4
-      dict.get('EncryptMetadata') !== false); // makes true as default value
+    // meaningful when V is 4 or 5
+    var encryptMetadata = ((algorithm === 4 || algorithm === 5) &&
+                           dict.get('EncryptMetadata') !== false);
     this.encryptMetadata = encryptMetadata;
 
     var fileIdBytes = stringToBytes(fileId);
@@ -603,9 +1823,29 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
       passwordBytes = stringToBytes(password);
     }
 
-    var encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
-                                       ownerPassword, userPassword, flags,
-                                       revision, keyLength, encryptMetadata);
+    var encryptionKey;
+    if (algorithm !== 5) {
+      encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
+                                     ownerPassword, userPassword, flags,
+                                     revision, keyLength, encryptMetadata);
+    }
+    else {
+      var ownerValidationSalt = stringToBytes(dict.get('O')).subarray(32, 40);
+      var ownerKeySalt = stringToBytes(dict.get('O')).subarray(40, 48);
+      var uBytes = stringToBytes(dict.get('U')).subarray(0, 48);
+      var userValidationSalt = stringToBytes(dict.get('U')).subarray(32, 40);
+      var userKeySalt = stringToBytes(dict.get('U')).subarray(40, 48);
+      var ownerEncryption = stringToBytes(dict.get('OE'));
+      var userEncryption = stringToBytes(dict.get('UE'));
+      var perms = stringToBytes(dict.get('Perms'));
+      encryptionKey =
+        createEncryptionKey20(revision, passwordBytes,
+          ownerPassword, ownerValidationSalt,
+          ownerKeySalt, uBytes,
+          userPassword, userValidationSalt,
+          userKeySalt, ownerEncryption,
+          userEncryption, perms);
+    }
     if (!encryptionKey && !password) {
       throw new PasswordException('No password given',
                                   PasswordResponses.NEED_PASSWORD);
@@ -625,7 +1865,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
 
     this.encryptionKey = encryptionKey;
 
-    if (algorithm == 4) {
+    if (algorithm >= 4) {
       this.cf = dict.get('CF');
       this.stmf = dict.get('StmF') || identityName;
       this.strf = dict.get('StrF') || identityName;
@@ -659,28 +1899,33 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
     if (cryptFilter !== null && cryptFilter !== undefined) {
       cfm = cryptFilter.get('CFM');
     }
-    if (!cfm || cfm.name == 'None') {
+    if (!cfm || cfm.name === 'None') {
       return function cipherTransformFactoryBuildCipherConstructorNone() {
         return new NullCipher();
       };
     }
-    if ('V2' == cfm.name) {
+    if ('V2' === cfm.name) {
       return function cipherTransformFactoryBuildCipherConstructorV2() {
         return new ARCFourCipher(buildObjectKey(num, gen, key, false));
       };
     }
-    if ('AESV2' == cfm.name) {
+    if ('AESV2' === cfm.name) {
       return function cipherTransformFactoryBuildCipherConstructorAESV2() {
         return new AES128Cipher(buildObjectKey(num, gen, key, true));
       };
     }
+    if ('AESV3' === cfm.name) {
+      return function cipherTransformFactoryBuildCipherConstructorAESV3() {
+        return new AES256Cipher(key);
+      };
+    }
     error('Unknown crypto method');
   }
 
   CipherTransformFactory.prototype = {
     createCipherTransform:
       function CipherTransformFactory_createCipherTransform(num, gen) {
-      if (this.algorithm == 4) {
+      if (this.algorithm === 4 || this.algorithm === 5) {
         return new CipherTransform(
           buildCipherConstructor(this.cf, this.stmf,
                                  num, gen, this.encryptionKey),
@@ -697,5 +1942,4 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
   };
 
   return CipherTransformFactory;
-})();
-
+})();
\ No newline at end of file
diff --git a/test/unit/crypto_spec.js b/test/unit/crypto_spec.js
index e17f2b1..d59d321 100644
--- a/test/unit/crypto_spec.js
+++ b/test/unit/crypto_spec.js
@@ -1,7 +1,8 @@
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* globals expect, it, describe, calculateMD5, ARCFourCipher, Name,
-           CipherTransformFactory */
+           CipherTransformFactory, calculateSHA256, calculateSHA384,
+           calculateSHA512, AES128Cipher, AES256Cipher, PDF17, PDF20*/
 
 'use strict';
 
@@ -186,8 +187,309 @@ describe('crypto', function() {
       expect(result).toEqual(expected);
     });
   });
+
+  describe('calculateSHA256', function() {
+    it('should properly hash abc', function() {
+      var input, result, expected;
+      input = string2binary('abc');
+      result = calculateSHA256(input, 0, input.length);
+      expected = hex2binary('BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9C' +
+                            'B410FF61F20015AD');
+      expect(result).toEqual(expected);
+    });
+    it('should properly hash a multiblock input', function() {
+      var input, result, expected;
+      input = string2binary('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmno' +
+                            'mnopnopq');
+      result = calculateSHA256(input, 0, input.length);
+      expected = hex2binary('248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167' +
+                            'F6ECEDD419DB06C1');
+      expect(result).toEqual(expected);
+    });
+  });
+
+  describe('calculateSHA384', function() {
+    it('should properly hash abc', function() {
+      var input, result, expected;
+      input = string2binary('abc');
+      result = calculateSHA384(input, 0, input.length);
+      expected = hex2binary('CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED163' +
+                            '1A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7');
+      expect(result).toEqual(expected);
+    });
+    it('should properly hash a multiblock input', function() {
+      var input, result, expected;
+      input = string2binary('abcdefghbcdefghicdefghijdefghijkefghijklfghijklm' +
+                            'ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs' +
+                            'mnopqrstnopqrstu');
+      result = calculateSHA384(input, 0, input.length);
+      expected = hex2binary('09330C33F71147E83D192FC782CD1B4753111B173B3B05D2' +
+                            '2FA08086E3B0F712FCC7C71A557E2DB966C3E9FA91746039');
+      expect(result).toEqual(expected);
+    });
+  });
+
+  describe('calculateSHA512', function() {
+    it('should properly hash abc', function() {
+      var input, result, expected;
+      input = string2binary('abc');
+      result = calculateSHA512(input, 0, input.length);
+      expected = hex2binary('DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2' +
+                            '0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD' +
+                            '454D4423643CE80E2A9AC94FA54CA49F');
+      expect(result).toEqual(expected);
+    });
+    it('should properly hash a multiblock input', function() {
+      var input, result, expected;
+      input = string2binary('abcdefghbcdefghicdefghijdefghijkefghijklfghijklm' +
+                            'ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs' +
+                            'mnopqrstnopqrstu');
+      result = calculateSHA512(input, 0, input.length);
+      expected = hex2binary('8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA1' +
+                            '7299AEADB6889018501D289E4900F7E4331B99DEC4B5433A' +
+                            'C7D329EEB6DD26545E96E55B874BE909');
+      expect(result).toEqual(expected);
+    });
+  });
+
+
+
+  describe('AES128', function() {
+    describe('Encryption', function() {
+      it('should be able to encrypt a block', function() {
+        var input, key, result, expected, iv, cipher;
+        input = hex2binary('00112233445566778899aabbccddeeff');
+        key = hex2binary('000102030405060708090a0b0c0d0e0f');
+        iv = hex2binary('00000000000000000000000000000000');
+        cipher = new AES128Cipher(key);
+        result = cipher.encrypt(input,iv);
+        expected = hex2binary('69c4e0d86a7b0430d8cdb78070b4c55a');
+        expect(result).toEqual(expected);
+      });
+    });
+
+    describe('Decryption', function() {
+      it('should be able to decrypt a block with IV in stream', function() {
+        var input, key, result, expected, cipher;
+        input = hex2binary('0000000000000000000000000000000069c4e0d86a7b0430d' +
+                           '8cdb78070b4c55a');
+        key = hex2binary('000102030405060708090a0b0c0d0e0f');
+        cipher = new AES128Cipher(key);
+        result = cipher.decryptBlock(input);
+        expected = hex2binary('00112233445566778899aabbccddeeff');
+        expect(result).toEqual(expected);
+      });
+    });
+  });
+
+  describe('AES256', function() {
+    describe('Encryption', function() {
+      it('should be able to encrypt a block', function() {
+        var input, key, result, expected, iv, cipher;
+        input = hex2binary('00112233445566778899aabbccddeeff');
+        key = hex2binary('000102030405060708090a0b0c0d0e0f101112131415161718' +
+                          '191a1b1c1d1e1f');
+        iv = hex2binary('00000000000000000000000000000000');
+        cipher = new AES256Cipher(key);
+        result = cipher.encrypt(input,iv);
+        expected = hex2binary('8ea2b7ca516745bfeafc49904b496089');
+        expect(result).toEqual(expected);
+      });
+    });
+
+    describe('Decryption', function() {
+      it('should be able to decrypt a block with specified iv', function() {
+        var input, key, result, expected, cipher, iv;
+        input = hex2binary('8ea2b7ca516745bfeafc49904b496089');
+        key = hex2binary('000102030405060708090a0b0c0d0e0f101112131415161718' +
+                          '191a1b1c1d1e1f');
+        iv = hex2binary('00000000000000000000000000000000');
+        cipher = new AES256Cipher(key);
+        result = cipher.decryptBlock(input,false,iv);
+        expected = hex2binary('00112233445566778899aabbccddeeff');
+        expect(result).toEqual(expected);
+      });
+      it('should be able to decrypt a block with IV in stream', function() {
+        var input, key, result, expected, cipher;
+        input = hex2binary('000000000000000000000000000000008ea2b7ca516745bf' +
+                           'eafc49904b496089');
+        key = hex2binary('000102030405060708090a0b0c0d0e0f101112131415161718' +
+                           '191a1b1c1d1e1f');
+        cipher = new AES256Cipher(key);
+        result = cipher.decryptBlock(input,false);
+        expected = hex2binary('00112233445566778899aabbccddeeff');
+        expect(result).toEqual(expected);
+      });
+    });
+  });
+
+  describe('PDF17Algorithm', function() {
+    it('should correctly check a user key', function() {
+      var password, userValidation, userPassword, alg, result;
+      alg = new PDF17();
+      password = new Uint8Array([117, 115, 101, 114]);
+      userValidation = new Uint8Array([117, 169, 4, 32, 159, 101, 22, 220]);
+      userPassword = new Uint8Array([
+                                      131, 242, 143, 160, 87, 2, 138, 134, 79,
+                                      253, 189, 173, 224, 73, 144, 241, 190, 81,
+                                      197, 15, 249, 105, 145, 151, 15, 194, 65,
+                                      3, 1, 126, 187, 221]);
+      result = alg.checkUserPassword(password, userValidation, userPassword);
+      expect(result).toEqual(true);
+    });
+
+    it('should correctly check an owner key', function () {
+      var password, ownerValidation, ownerPassword, alg, result, uBytes;
+      alg = new PDF17();
+      password = new Uint8Array([111, 119, 110, 101, 114]);
+      ownerValidation = new Uint8Array([243, 118, 71, 153, 128, 17, 101, 62]);
+      ownerPassword = new Uint8Array([
+                                       60, 98, 137, 35, 51, 101, 200, 152, 210,
+                                       178, 226, 228, 134, 205, 163, 24, 204,
+                                       126, 177, 36, 106, 50, 36, 125, 210, 172,
+                                       171, 120, 222, 108, 139, 115]);
+      uBytes = new Uint8Array([
+                                131, 242, 143, 160, 87, 2, 138, 134, 79, 253,
+                                189, 173, 224, 73, 144, 241, 190, 81, 197, 15,
+                                249, 105, 145, 151, 15, 194, 65, 3, 1, 126, 187,
+                                221, 117, 169, 4, 32, 159, 101, 22, 220, 168,
+                                94, 215, 192, 100, 38, 188, 40]);
+      result = alg.checkOwnerPassword(password, ownerValidation, uBytes,
+                                      ownerPassword);
+      expect(result).toEqual(true);
+    });
+
+    it('should generate a file encryption key from the user key', function () {
+      var password, userKeySalt, expected, alg, result, userEncryption;
+      alg = new PDF17();
+      password = new Uint8Array([117, 115, 101, 114]);
+      userKeySalt = new Uint8Array([168, 94, 215, 192, 100, 38, 188, 40]);
+      userEncryption = new Uint8Array([
+                                        35, 150, 195, 169, 245, 51, 51, 255,
+                                        158, 158, 33, 242, 231, 75, 125, 190,
+                                        25, 126, 172, 114, 195, 244, 137, 245,
+                                        234, 165, 42, 74, 60, 38, 17, 17]);
+      result = alg.getUserKey(password, userKeySalt, userEncryption);
+      expected = new Uint8Array([
+                                  63, 114, 136, 209, 87, 61, 12, 30, 249, 1,
+                                  186, 144, 254, 248, 163, 153, 151, 51, 133,
+                                  10, 80, 152, 206, 15, 72, 187, 231, 33, 224,
+                                  239, 13, 213]);
+      expect(result).toEqual(expected);
+    });
+
+    it('should generate a file encryption key from the owner key', function () {
+      var password, ownerKeySalt, expected, alg, result, ownerEncryption;
+      var uBytes;
+      alg = new PDF17();
+      password = new Uint8Array([111, 119, 110, 101, 114]);
+      ownerKeySalt = new Uint8Array([200, 245, 242, 12, 218, 123, 24, 120]);
+      ownerEncryption = new Uint8Array([
+                                         213, 202, 14, 189, 110, 76, 70, 191, 6,
+                                         195, 10, 190, 157, 100, 144, 85, 8, 62,
+                                         123, 178, 156, 229, 50, 40, 229, 216,
+                                         54, 222, 34, 38, 106, 223]);
+      uBytes = new Uint8Array([
+                                131, 242, 143, 160, 87, 2, 138, 134, 79, 253,
+                                189, 173, 224, 73, 144, 241, 190, 81, 197, 15,
+                                249, 105, 145, 151, 15, 194, 65, 3, 1, 126, 187,
+                                221, 117, 169, 4, 32, 159, 101, 22, 220, 168,
+                                94, 215, 192, 100, 38, 188, 40]);
+      result = alg.getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption);
+      expected = new Uint8Array([
+                                  63, 114, 136, 209, 87, 61, 12, 30, 249, 1,
+                                  186, 144, 254, 248, 163, 153, 151, 51, 133,
+                                  10, 80, 152, 206, 15, 72, 187, 231, 33, 224,
+                                  239, 13, 213]);
+      expect(result).toEqual(expected);
+    });
+  });
+
+  describe('PDF20Algorithm', function() {
+    it('should correctly check a user key', function () {
+      var password, userValidation, userPassword, alg, result;
+      alg = new PDF20();
+      password = new Uint8Array([117, 115, 101, 114]);
+      userValidation = new Uint8Array([83, 245, 146, 101, 198, 247, 34, 198]);
+      userPassword = new Uint8Array([
+                                      94, 230, 205, 75, 166, 99, 250, 76, 219,
+                                      128, 17, 85, 57, 17, 33, 164, 150, 46,
+                                      103, 176, 160, 156, 187, 233, 166, 223,
+                                      163, 253, 147, 235, 95, 184]);
+      result = alg.checkUserPassword(password, userValidation, userPassword);
+      expect(result).toEqual(true);
+    });
+
+    it('should correctly check an owner key', function () {
+      var password, ownerValidation, ownerPassword, alg, result, uBytes;
+      alg = new PDF20();
+      password = new Uint8Array([111, 119, 110, 101, 114]);
+      ownerValidation = new Uint8Array([142, 232, 169, 208, 202, 214, 5, 185]);
+      ownerPassword = new Uint8Array([
+                                       88, 232, 62, 54, 245, 26, 245, 209, 137,
+                                       123, 221, 72, 199, 49, 37, 217, 31, 74,
+                                       115, 167, 127, 158, 176, 77, 45, 163, 87,
+                                       47, 39, 90, 217, 141]);
+      uBytes = new Uint8Array([
+                                94, 230, 205, 75, 166, 99, 250, 76, 219, 128,
+                                17, 85, 57, 17, 33, 164, 150, 46, 103, 176, 160,
+                                156, 187, 233, 166, 223, 163, 253, 147, 235, 95,
+                                184, 83, 245, 146, 101, 198, 247, 34, 198, 191,
+                                11, 16, 94, 237, 216, 20, 175]);
+      result = alg.checkOwnerPassword(password, ownerValidation, uBytes,
+                                      ownerPassword);
+      expect(result).toEqual(true);
+    });
+
+    it('should generate a file encryption key from the user key', function () {
+      var password, userKeySalt, expected, alg, result, userEncryption;
+      alg = new PDF20();
+      password = new Uint8Array([117, 115, 101, 114]);
+      userKeySalt = new Uint8Array([191, 11, 16, 94, 237, 216, 20, 175]);
+      userEncryption = new Uint8Array([
+                                        121, 208, 2, 181, 230, 89, 156, 60, 253,
+                                        143, 212, 28, 84, 180, 196, 177, 173,
+                                        128, 221, 107, 46, 20, 94, 186, 135, 51,
+                                        95, 24, 20, 223, 254, 36]);
+      result = alg.getUserKey(password, userKeySalt, userEncryption);
+      expected = new Uint8Array([
+                                  42, 218, 213, 39, 73, 91, 72, 79, 67, 38, 248,
+                                  133, 18, 189, 61, 34, 107, 79, 29, 56, 59,
+                                  181, 213, 118, 113, 34, 65, 210, 87, 174, 22,
+                                  239]);
+      expect(result).toEqual(expected);
+    });
+
+    it('should generate a file encryption key from the owner key', function () {
+      var password, ownerKeySalt, expected, alg, result, ownerEncryption;
+      var uBytes;
+      alg = new PDF20();
+      password = new Uint8Array([111, 119, 110, 101, 114]);
+      ownerKeySalt = new Uint8Array([29, 208, 185, 46, 11, 76, 135, 149]);
+      ownerEncryption = new Uint8Array([
+                                         209, 73, 224, 77, 103, 155, 201, 181,
+                                         190, 68, 223, 20, 62, 90, 56, 210, 5,
+                                         240, 178, 128, 238, 124, 68, 254, 253,
+                                         244, 62, 108, 208, 135, 10, 251]);
+      uBytes = new Uint8Array([
+                                94, 230, 205, 75, 166, 99, 250, 76, 219, 128,
+                                17, 85, 57, 17, 33, 164, 150, 46, 103, 176, 160,
+                                156, 187, 233, 166, 223, 163, 253, 147, 235, 95,
+                                184, 83, 245, 146, 101, 198, 247, 34, 198, 191,
+                                11, 16, 94, 237, 216, 20, 175]);
+      result = alg.getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption);
+      expected = new Uint8Array([
+                                  42, 218, 213, 39, 73, 91, 72, 79, 67, 38, 248,
+                                  133, 18, 189, 61, 34, 107, 79, 29, 56, 59,
+                                  181, 213, 118, 113, 34, 65, 210, 87, 174, 22,
+                                  239]);
+      expect(result).toEqual(expected);
+    });
+  });
 });
 
+
+
 describe('CipherTransformFactory', function() {
   function DictMock(map) {
     this.map = map;
@@ -222,9 +524,136 @@ describe('CipherTransformFactory', function() {
     P: -1084,
     R: 4
   };
+
+  var aes256Map = {
+    Filter: Name.get('Standard'),
+    V: 5,
+    Length: 256,
+    O: unescape('%3Cb%89%233e%C8%98%D2%B2%E2%E4%86%CD%A3%18%CC%7E%B1%24j2%24%' +
+                '7D%D2%AC%ABx%DEl%8Bs%F3vG%99%80%11e%3E%C8%F5%F2%0C%DA%7B%18x'),
+    U: unescape('%83%F2%8F%A0W%02%8A%86O%FD%BD%AD%E0I%90%F1%BEQ%C5%0F%F9i%91%' +
+                '97%0F%C2A%03%01%7E%BB%DDu%A9%04%20%9Fe%16%DC%A8%5E%D7%C0d%26' +
+                '%BC%28'),
+    OE: unescape('%D5%CA%0E%BDnLF%BF%06%C3%0A%BE%9Dd%90U%08%3E%7B%B2%9C%E52%2' +
+                '8%E5%D86%DE%22%26j%DF'),
+    UE: unescape('%23%96%C3%A9%F533%FF%9E%9E%21%F2%E7K%7D%BE%19%7E%ACr%C3%F4%' +
+                 '89%F5%EA%A5*J%3C%26%11%11'),
+    Perms: unescape('%D8%FC%844%E5e%0DB%5D%7Ff%FD%3COMM'),
+    P: -1084,
+    R: 5
+  };
+
+  var aes256IsoMap = {
+    Filter: Name.get('Standard'),
+    V: 5,
+    Length: 256,
+    O: unescape('X%E8%3E6%F5%1A%F5%D1%89%7B%DDH%C71%25%D9%1FJs%A7%7F%9E%B0M-%' +
+                'A3W/%27Z%D9%8D%8E%E8%A9%D0%CA%D6%05%B9%1D%D0%B9.%0BL%87%95'),
+    U: unescape('%5E%E6%CDK%A6c%FAL%DB%80%11U9%11%21%A4%96.g%B0%A0%9C%BB%E9%A' +
+                '6%DF%A3%FD%93%EB_%B8S%F5%92e%C6%F7%22%C6%BF%0B%10%5E%ED%D8%1' +
+                '4%AF'),
+    OE: unescape('%D1I%E0Mg%9B%C9%B5%BED%DF%14%3EZ8%D2%05%F0%B2%80%EE%7CD%FE%' +
+                 'FD%F4%3El%D0%87%0A%FB'),
+    UE: unescape('y%D0%02%B5%E6Y%9C%3C%FD%8F%D4%1CT%B4%C4%B1%AD%80%DDk.%14%5E' +
+                 '%BA%873_%18%14%DF%FE%24'),
+    Perms: unescape('l%AD%0F%A0%EBM%86WM%3E%CB%B5%E0X%C97'),
+    P: -1084,
+    R: 6
+  };
+
+  var aes256BlankMap = {
+    Filter: Name.get('Standard'),
+    V: 5,
+    Length: 256,
+    O: unescape('%B8p%04%C3g%26%FCW%CCN%D4%16%A1%E8%950YZ%C9%9E%B1-%97%F3%FE%' +
+                '03%13%19ffZn%8F%F5%EB%EC%CC5sV%10e%CEl%B5%E9G%C1'),
+    U: unescape('%83%D4zi%F1O0%961%12%CC%82%CB%CA%BF5y%FD%21%EB%E4%D1%B5%1D%D' +
+                '6%FA%14%F3%BE%8Fqs%EF%88%DE%E2%E8%DC%F55%E4%B8%16%C8%14%8De%' +
+                '1E'),
+    OE: unescape('%8F%19%E8%D4%27%D5%07%CA%C6%A1%11%A6a%5Bt%F4%DF%0F%84%29%0F' +
+                 '%E4%EFF7%5B%5B%11%A0%8F%17e'),
+    UE: unescape('%81%F5%5D%B0%28%81%E4%7F_%7C%8F%85b%A0%7E%10%D0%88lx%7B%7EJ' +
+                 '%5E%912%B6d%12%27%05%F6'),
+    Perms: unescape('%86%1562%0D%AE%A2%FB%5D%3B%22%3Dq%12%B2H'),
+    P: -1084,
+    R: 5
+  };
+
+  var aes256IBlankMap = {
+    Filter: Name.get('Standard'),
+    V: 5,
+    Length: 256,
+    O: unescape('%F7%DB%99U%A6M%ACk%AF%CF%D7AFw%E9%C1%91%CBDgI%23R%CF%0C%15r%' +
+                'D74%0D%CE%E9%91@%E4%98QF%BF%88%7Ej%DE%AD%8F%F4@%C1'),
+    U: unescape('%1A%A9%DC%918%83%93k%29%5B%117%B16%DB%E8%8E%FE%28%E5%89%D4%0' +
+                'E%AD%12%3B%7DN_6fez%8BG%18%05YOh%7DZH%A3Z%87%17*'),
+    OE: unescape('%A4a%88%20h%1B%7F%CD%D5%CAc%D8R%83%E5%D6%1C%D2%98%07%984%BA' +
+                 '%AF%1B%B4%7FQ%F8%1EU%7D'),
+    UE: unescape('%A0%0AZU%27%1D%27%2C%0B%FE%0E%A2L%F9b%5E%A1%B9%D6v7b%B26%A9' +
+                 'N%99%F1%A4Deq'),
+    Perms: unescape('%03%F2i%07%0D%C3%F9%F2%28%80%B7%F5%DD%D1c%EB'),
+    P: -1084,
+    R: 6
+  };
+
   var fileID2 = unescape('%3CL_%3AD%96%AF@%9A%9D%B3%3Cx%1Cv%AC');
 
   describe('#ctor', function() {
+    describe('AES256 Revision 5', function () {
+      it('should accept user password', function () {
+        var factory = new CipherTransformFactory(new DictMock(aes256Map),
+                                                 fileID1,
+                                                 'user');
+      });
+      it('should accept owner password', function () {
+        var factory = new CipherTransformFactory(new DictMock(aes256Map),
+                                                 fileID1,
+                                                 'owner');
+      });
+      it('should not accept wrong password', function () {
+        var thrown = false;
+        try {
+          var factory = new CipherTransformFactory(new DictMock(aes256Map),
+                                                   fileID1,
+                                                   'wrong');
+        } catch (e) {
+          thrown = true;
+        }
+        expect(thrown).toEqual(true);
+      });
+      it('should accept blank password', function () {
+        var factory = new CipherTransformFactory(new DictMock(aes256BlankMap),
+                                                 fileID1);
+      });
+    });
+
+    describe('AES256 Revision 6', function () {
+      it('should accept user password', function () {
+        var factory = new CipherTransformFactory(new DictMock(aes256IsoMap),
+                                                 fileID1,
+                                                 'user');
+      });
+      it('should accept owner password', function () {
+        var factory = new CipherTransformFactory(new DictMock(aes256IsoMap),
+                                                 fileID1,
+                                                 'owner');
+      });
+      it('should not accept wrong password', function () {
+        var thrown = false;
+        try {
+          var factory = new CipherTransformFactory(new DictMock(aes256IsoMap),
+                                                   fileID1,
+                                                   'wrong');
+        } catch (e) {
+          thrown = true;
+        }
+        expect(thrown).toEqual(true);
+      });
+      it('should accept blank password', function () {
+        var factory = new CipherTransformFactory(new DictMock(aes256IBlankMap),
+                                                 fileID1);
+      });
+    });
     it('should accept user password', function() {
       var factory = new CipherTransformFactory(new DictMock(map1), fileID1,
                                                '123456');

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/pdf.js.git



More information about the Pkg-javascript-commits mailing list