| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| goog.module('tink.subtle.AesCtrTest'); |
| goog.setTestOnly('tink.subtle.AesCtrTest'); |
| |
| const AesCtr = goog.require('tink.subtle.AesCtr'); |
| const Bytes = goog.require('tink.subtle.Bytes'); |
| const Random = goog.require('tink.subtle.Random'); |
| const TestCase = goog.require('goog.testing.TestCase'); |
| const testSuite = goog.require('goog.testing.testSuite'); |
| |
| testSuite({ |
| |
| setUp() { |
| // Use a generous promise timeout for running continuously. |
| TestCase.getActiveTestCase().promiseTimeout = 1000 * 1000; // 1000s |
| }, |
| |
| tearDown() { |
| // Reset the timeout. |
| TestCase.getActiveTestCase().promiseTimeout = 1000; // 1s |
| }, |
| |
| async testBasic() { |
| // Set longer time for promiseTimout as the test sometimes takes longer than |
| // 1 second in Firefox. |
| const key = Random.randBytes(16); |
| for (let i = 0; i < 100; i++) { |
| const msg = Random.randBytes(20); |
| const cipher = await AesCtr.newInstance(key, 16); |
| let ciphertext = await cipher.encrypt(msg); |
| let plaintext = await cipher.decrypt(ciphertext); |
| assertEquals(Bytes.toHex(msg), Bytes.toHex(plaintext)); |
| } |
| }, |
| |
| async testProbabilisticEncryption() { |
| const cipher = await AesCtr.newInstance(Random.randBytes(16), 16); |
| const msg = Random.randBytes(20); |
| const aad = Random.randBytes(20); |
| const results = new Set(); |
| for (let i = 0; i < 100; i++) { |
| const ciphertext = await cipher.encrypt(msg, aad); |
| results.add(Bytes.toHex(ciphertext)); |
| } |
| assertEquals(100, results.size); |
| }, |
| |
| async testConstructor() { |
| try { |
| await AesCtr.newInstance(123, 16); // IV size too short |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: input must be a non null Uint8Array', e.toString()); |
| } |
| try { |
| await AesCtr.newInstance(Random.randBytes(16), 11); // IV size too short |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: invalid IV length, must be at least 12 and at most 16', |
| e.toString()); |
| } |
| try { |
| await AesCtr.newInstance(Random.randBytes(16), 17); // IV size too long |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: invalid IV length, must be at least 12 and at most 16', |
| e.toString()); |
| } |
| try { |
| await AesCtr.newInstance( |
| Random.randBytes(24), 12); // 192-bit keys not supported |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals('CustomError: unsupported AES key size: 24', e.toString()); |
| } |
| }, |
| |
| async testConstructor_invalidIvSizes() { |
| try { |
| await AesCtr.newInstance(Random.randBytes(16), NaN); |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: invalid IV length, must be an integer', e.toString()); |
| } |
| |
| try { |
| await AesCtr.newInstance(Random.randBytes(16), undefined); |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: invalid IV length, must be an integer', e.toString()); |
| } |
| |
| try { |
| await AesCtr.newInstance(Random.randBytes(16), 12.5); |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: invalid IV length, must be an integer', e.toString()); |
| } |
| |
| try { |
| await AesCtr.newInstance(Random.randBytes(16), 0); |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: invalid IV length, must be at least 12 and at most 16', |
| e.toString()); |
| } |
| }, |
| |
| async testType() { |
| try { |
| await AesCtr.newInstance('blah', 12); |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: input must be a non null Uint8Array', e.toString()); |
| } |
| |
| const cipher = await AesCtr.newInstance(Random.randBytes(16), 12); |
| try { |
| await cipher.encrypt('blah'); |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: input must be a non null Uint8Array', e.toString()); |
| } |
| try { |
| await cipher.encrypt(123); |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: input must be a non null Uint8Array', e.toString()); |
| } |
| try { |
| await cipher.decrypt('blah'); |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: input must be a non null Uint8Array', e.toString()); |
| } |
| try { |
| await cipher.decrypt(123); |
| fail('Should throw an exception.'); |
| } catch (e) { |
| assertEquals( |
| 'CustomError: input must be a non null Uint8Array', e.toString()); |
| } |
| }, |
| |
| async testWithTestVectors() { |
| // Test data from NIST SP 800-38A pp 55. |
| const NIST_TEST_VECTORS = [ |
| { |
| 'key': '2b7e151628aed2a6abf7158809cf4f3c', |
| 'message': |
| '6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51' + |
| '30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710', |
| 'ciphertext': |
| '874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff' + |
| '5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee', |
| 'iv': 'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff' |
| }, |
| ]; |
| for (let i = 0; i < NIST_TEST_VECTORS.length; i++) { |
| const testVector = NIST_TEST_VECTORS[i]; |
| const key = Bytes.fromHex(testVector['key']); |
| const iv = Bytes.fromHex(testVector['iv']); |
| const msg = Bytes.fromHex(testVector['message']); |
| const ciphertext = Bytes.fromHex(testVector['ciphertext']); |
| const aesctr = await AesCtr.newInstance(key, iv.length); |
| const plaintext = await aesctr.decrypt(Bytes.concat(iv, ciphertext)); |
| assertEquals(Bytes.toHex(msg), Bytes.toHex(plaintext)); |
| } |
| }, |
| }); |