user3775777
user3775777

Reputation: 11

AES encrypted data length using WebCrypto

What I'm trying to do is encrypt 16 byte packets of data using the 128 bit AES-CBC encryption provided by SubtleCrypto.encrypt() in a web browser.

What I expected to find was 16 bytes of encrypted data for 16 bytes of input.

What I actually find is 32 bytes of encrypted data for 16 bytes of input.

Specifically, 15 bytes of input produces 16 bytes of output, 16 bytes of input produces 32 bytes of output, and 17 bytes of data produces 32 bytes of output.

My question is, why does 16 bytes of input data produce 32 bytes of output? I would have suspected that this might happen only with > 16 bytes of input data, not >= 16 bytes.

Save the following test code to a file and open with a web browser.

<html>
<head>
<script>

  var myKey = window.crypto.getRandomValues(new Uint8Array(16));
  console.log("Raw key = " + myKey);
  var keyObj = {};
  var keyFormat = "raw"; 
  var extractable = false;
  var usages = ["encrypt", "decrypt"];
  window.crypto.subtle.importKey(keyFormat, myKey, "AES-CBC", extractable, usages)
  .then(function(importedKey) {
    keyObj = importedKey;
    console.log("Encryption/Decryption key object = " + keyObj);

    var vector = window.crypto.getRandomValues(new Uint8Array(16));

    var encryptThis15 = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
    var encryptThis16 = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    var encryptThis17 = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]);

    var decryptSuccess = function(decrypted) {
      var decryptedData = new Uint8Array(decrypted);
      console.log("Decrypted data = " + decryptedData.length + " bytes: " + decryptedData);
    };

    var decryptFail = function(error) {
      // Fail.
      console.log("Failure decrypting data.  Error: " + error);
    };

    var encryptSuccess = function(encData) {
      var encryptedData = new Uint8Array(encData);
      console.log("Encrypted data = " + encryptedData.length + " bytes: " + encryptedData);
      return encData;
    };

    var encryptFail = function(error) {
      console.log("Failure encrypting data.  Error: " + error);
    };

    console.log("15 byte data array as input = " + encryptThis15);

    window.crypto.subtle.encrypt({name: "AES-CBC", iv: vector}, keyObj, encryptThis15)
    .then(encryptSuccess)
    .then(function(encrypted){return window.crypto.subtle.decrypt({name: "AES-CBC", iv: vector}, keyObj, encrypted);})
    .then(decryptSuccess)
    .catch(decryptFail);

    console.log("16 byte data array as input = " + encryptThis16);

    window.crypto.subtle.encrypt({name: "AES-CBC", iv: vector}, keyObj, encryptThis16)
    .then(encryptSuccess)
    .then(function(encrypted){return window.crypto.subtle.decrypt({name: "AES-CBC", iv: vector}, keyObj, encrypted);})
    .then(decryptSuccess)
    .catch(decryptFail);

    console.log("17 byte data array as input = " + encryptThis17);

    window.crypto.subtle.encrypt({name: "AES-CBC", iv: vector}, keyObj, encryptThis17)
    .then(encryptSuccess)
    .then(function(encrypted){return window.crypto.subtle.decrypt({name: "AES-CBC", iv: vector}, keyObj, encrypted);})
    .then(decryptSuccess)
    .catch(decryptFail);

  })
  .catch(function(err){
    console.log("Key generation error = " + err);
  });

</script>
<title>WebCrypto encrypt test</title>
</head>
<body>
<p>See console log.</p>
</body>
</html>

Upvotes: 1

Views: 966

Answers (1)

Luke Joshua Park
Luke Joshua Park

Reputation: 9806

PKCS#5/7 padding dictates that blocks must append a bytes of the byte a to the plaintext until the block is complete. That is, if you are trying to encrypt:

  • 15 bytes: the padding becomes 01
  • 14 bytes: the padding becomes 02 02
  • 13 bytes: the padding becomes 03 03 03

If you are using this padding mode (which you are) and you encrypt data that is the same size as the block size (16 bytes for you), then the block is not padded and decrypting with the same padding mode will fail. Thus, you must add 16 bytes of 0x10 to the end of the plaintext.

Upvotes: 1

Related Questions