summaryrefslogtreecommitdiffstats
path: root/third_party/WebKit/LayoutTests/crypto/encrypt-decrypt.html
blob: 89923ea1b7c3e2ac2b2c56c4234b71280c91287e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
<!DOCTYPE html>
<html>
<head>
<script src="../resources/js-test.js"></script>
<script src="resources/common.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>

<script>
description("Tests cypto.subtle.encrypt and crypto.subtle.decrypt");

jsTestIsAsync = true;

// A list of Promises for every test to run.
var allTests = [];

// -------------------------------------------------
// Successful encryption/decryption
// -------------------------------------------------

// Test vectors marked with [1] were copied from:
//   http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
//
// The NIST tests do not have a padding block. To match the WebCrypto
// expectations, a PKCS#5 padding block has been added.

var kSuccessTestVectors = [
  // 128-bit key with plaintext that is an exact multiple of block size.
  // Derived from [1] F.2.1 (CBC-AES128.Encrypt), by adding padding block.
  {
    key: "2b7e151628aed2a6abf7158809cf4f3c",
    iv: "000102030405060708090a0b0c0d0e0f",
    plainText: "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
    cipherText: "7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a7" +
                // Padding block.
                "8cb82807230e1321d3fae00d18cc2012"
  },

  // 192-bit key, where final block of plaintext has to pad by 15.
  // Derived from [1] F.2.3 (CBC-AES192.Encrypt), by stripping 15 bytes off
  // plaintext and adding padding block.
  {
    key: "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
    iv: "000102030405060708090a0b0c0d0e0f",
    plainText: "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff6",
    cipherText: "4f021db243bc633d7178183a9fa071e8b4d9ada9ad7dedf4e5e738763f69145a571b242012fb7ae07fa9baac3df102e0" +
                // Padding block.
                "288c6f9ec554652e50ab55e121f099ae"
  },

  // 256-bit key, where final block of plaintext has to pad by 3.
  // Derived from [1] F.2.6 CBC-AES256.Decrypt, by stripping 3 bytes off
  // plaintext and adding padding block.
  {
    key: "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
    iv: "000102030405060708090a0b0c0d0e0f",
    plainText: "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be6",
    cipherText: "f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461c9aaf02a6a54e9e242ccbf48c59daca6"
  },

  // 128-bit key, with empty plaintext.
  // Derived from Chromium's EncryptorTest.EmptyEncrypt() (encryptor_unittest.cc)
  {
    key: "3132383d5369787465656e4279746573",
    iv: "5377656574205369787465656e204956",
    plainText: "",
    cipherText: "8518b8878d34e7185e300d0fcc426396"
  },
];

function runSuccessTestCase(testCase)
{
    var algorithm = {name: 'aes-cbc', iv: hexStringToUint8Array(testCase.iv)};

    var key = null;
    var keyData = hexStringToUint8Array(testCase.key);
    var usages = ['encrypt', 'decrypt'];
    var extractable = false;

    // (1) Import the key
    return crypto.subtle.importKey('raw', keyData, algorithm, extractable, usages).then(function(result) {
        key = result;
        
        // shouldBe() can only resolve variables in global context.
        tmpKey = key;
        shouldBe("tmpKey.type", "'secret'");
        shouldBe("tmpKey.extractable", "false");
        shouldBe("tmpKey.algorithm.name", "'AES-CBC'");
        shouldBe("tmpKey.usages.join(',')", "'decrypt,encrypt'");

        // (2) Encrypt.
        return crypto.subtle.encrypt(algorithm, key, hexStringToUint8Array(testCase.plainText));
    }).then(function(result) {
        bytesShouldMatchHexString("Encryption", testCase.cipherText, result);

        // (3) Decrypt
        return crypto.subtle.decrypt(algorithm, key, hexStringToUint8Array(testCase.cipherText));
    }).then(function(result) {
        bytesShouldMatchHexString("Decryption", testCase.plainText, result);
    });
}

// Add all of the tests defined above.
for (var i = 0; i < kSuccessTestVectors.length; ++i) {
    addTask(runSuccessTestCase(kSuccessTestVectors[i]));
}

// -------------------------------------------------
// Failed key import.
// -------------------------------------------------

// Supported key lengths are 16 (128-bit), 32 (256-bit), 24 (192-bit),
// Try key lengths that are off by 1 from the supported ones.
var kUnsupportedKeyLengths = [
    0, 1, 15, 17, 31, 33, 23, 25, 64
];

function testInvalidKeyImport(keyLengthBytes)
{
    var algorithm = {name: 'aes-cbc'};
    var keyData = new Uint8Array(keyLengthBytes);

    var usages = ['encrypt', 'decrypt'];
    var extractable = false;

    return crypto.subtle.importKey('raw', keyData, algorithm, extractable, usages).then(function(result) {
        debug("FAIL: Successfully import key of length " + keyData.byteLength + " bytes");
    }, function(result) {
        debug("PASS: Failed to import key of length " + keyData.byteLength + " bytes");
    });
}

for (var i = 0; i < kUnsupportedKeyLengths.length; ++i) {
    addTask(testInvalidKeyImport(kUnsupportedKeyLengths[i]));
}

// -------------------------------------------------
// Invalid cipher texts
// -------------------------------------------------

function testInvalidDecryptions()
{
    // 128-bit key with plaintext that is an exact multiple of block size.
    // Derived from [1] F.2.1 (CBC-AES128.Encrypt), by adding padding block.
    var iv = hexStringToUint8Array("000102030405060708090a0b0c0d0e0f");
    var keyData = hexStringToUint8Array("2b7e151628aed2a6abf7158809cf4f3c");
    var cipherText = hexStringToUint8Array("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a78cb82807230e1321d3fae00d18cc2012");

    var key = null;
    var usages = ['encrypt', 'decrypt'];
    var extractable = false;
    var algorithm = {name: 'aes-cbc', iv: iv};

    function verifyDecryptionFails(newCipherTextLength)
    {
        var newCipherText = cipherText.subarray(0, newCipherTextLength);

        var description = "ciphertext length: " + newCipherText.byteLength;
        return crypto.subtle.decrypt(algorithm, key, newCipherText).then(function(result) {
            debug("FAIL: decrypting succeeded. " + description);
        }, function(result) {
            debug("PASS: decrypting failed. " + description);
        });
    }

    return crypto.subtle.importKey('raw', keyData, algorithm, extractable, usages).then(function(result) {
        key = result;

        // Verify that decryption works with the original ciphertext.
        return crypto.subtle.decrypt(algorithm, key, cipherText);
    }).then(function(result) {
      debug("PASS: Decryption succeeded");

      // Try a number of bad ciphertexts.
      return Promise.all([
          verifyDecryptionFails(0),
          verifyDecryptionFails(cipherText.byteLength - 1),

          // Stripped a whole block. This new final block will result in a
          // padding error.
          verifyDecryptionFails(cipherText.byteLength - 16),
          verifyDecryptionFails(1),
          verifyDecryptionFails(15),
          verifyDecryptionFails(16),
          verifyDecryptionFails(17),
      ]);
    });
}

addTask(testInvalidDecryptions());

function testNormalizationFailures(importedKeys)
{
    keys = importedKeys;

    data = asciiToUint8Array("hello");

    // ---------------------------------------------------
    // AES-CBC normalization failures (AesCbcParams)
    // ---------------------------------------------------

    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CBC', iv: null}, keys.aesCbc, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CBC'}, keys.aesCbc, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CBC', iv: 3}, keys.aesCbc, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CBC', iv: new Uint8Array(0)}, keys.aesCbc, data)");

    // ---------------------------------------------------
    // AES-CTR normalization failures (AesCtrParams)
    // ---------------------------------------------------

    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CTR', counter: null}, keys.aesCtr, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CTR'}, keys.aesCtr, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CTR', counter: new Uint8Array(0)}, keys.aesCtr, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CTR', counter: new Uint8Array(16), length: 0}, keys.aesCtr, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CTR', counter: new Uint8Array(16), length: 18}, keys.aesCtr, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CTR', counter: new Uint8Array(16), length: 256}, keys.aesCtr, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CTR', counter: new Uint8Array(16), length: -3}, keys.aesCtr, data)");
    shouldRejectPromiseWithNull("crypto.subtle.encrypt({name: 'AES-CTR', counter: new Uint8Array(16), length: Infinity}, keys.aesCtr, data)");

    // Try calling with the wrong key type.
    aesCbc = {name: 'AES-CBC', iv: new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])};
    shouldRejectPromiseWithNull("crypto.subtle.encrypt(aesCbc, keys.hmacSha1, data)");

    // Key doesn't support encrypt.
    shouldRejectPromiseWithNull("crypto.subtle.encrypt(aesCbc, keys.aesCbcJustDecrypt, data)");

    // If no key was specified AND the algorithm was bogus, should complain
    // about the missing key first.
    shouldThrow("crypto.subtle.encrypt({name: 'bogus'}, null, data)");
}

addTask(importTestKeys().then(testNormalizationFailures));

completeTestWhenAllTasksDone();

</script>

</body>