Project import generated by Copybara.
PiperOrigin-RevId: 191113399 GitOrigin-RevId: cfebd59d1f086e907f468004c7d033199d116fad
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for AES-GCM implementations of Web Crypto API.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TODO(thanhb):
|
||||
* - Tests invalid parameters
|
||||
*/
|
||||
|
||||
goog.provide('wycheproof.webcryptoapi.AES-GCM');
|
||||
goog.require('goog.testing.TestCase');
|
||||
goog.require('goog.testing.asserts');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.userAgent.product');
|
||||
goog.require('wycheproof.BigInteger');
|
||||
goog.require('wycheproof.TestUtil');
|
||||
|
||||
var TestUtil = wycheproof.TestUtil;
|
||||
var BigInteger = wycheproof.BigInteger;
|
||||
|
||||
// Test vector file
|
||||
var AES_GCM_VECTOR_FILE = '../../testvectors/aes_gcm_test.json';
|
||||
|
||||
|
||||
/**
|
||||
* A class containing a AES-GCM test case's parameters.
|
||||
* @param {!number} id Identifier of the test case
|
||||
* @param {!string} k The key in Base64URL format
|
||||
* @param {!number} keySize The key's size in bits
|
||||
* @param {!string} tag The expected tag of the encryption in hex format
|
||||
* @param {!number} tagSize The tag size in bits
|
||||
* @param {!ArrayBuffer} iv The initialization vector
|
||||
* @param {!number} ivSize The iv's size in bits
|
||||
* @param {!ArrayBuffer} aad The additional authentication data
|
||||
* @param {!ArrayBuffer} msg The message to be encrypted
|
||||
* @param {!string} ct The expected ciphertext of the encryption in hex format
|
||||
* @param {!string} result The expected result of the test
|
||||
*/
|
||||
var AesGcmTestCase = function(id, k, keySize, tag, tagSize, iv, ivSize,
|
||||
aad, msg, ct, result) {
|
||||
this.id = id;
|
||||
this.k = k;
|
||||
this.keySize = keySize;
|
||||
this.tag = tag;
|
||||
this.tagSize = tagSize;
|
||||
this.iv = iv;
|
||||
this.ivSize = ivSize;
|
||||
this.aad = aad;
|
||||
this.msg = msg;
|
||||
this.ct = ct;
|
||||
this.result = result;
|
||||
};
|
||||
|
||||
|
||||
/** AES-GCM wrapper */
|
||||
var AesGcm = function() {};
|
||||
|
||||
/**
|
||||
* Returns the AES-GCM algorithm name that is accepted by WebCrypto APIs.
|
||||
* @param {!number} keySize The key's size
|
||||
*
|
||||
* @return {!string} The algorithm's name
|
||||
*/
|
||||
AesGcm.getAlgName = function(keySize) {
|
||||
return 'A' + keySize + 'GCM';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Imports a AES-GCM key.
|
||||
* @param {!string} k The key in Base64URL format
|
||||
* @param {!number} keySize The key's size in bits
|
||||
*
|
||||
* @return {!Promise} A Promise containing the imported key
|
||||
*/
|
||||
AesGcm.importKey = function(k, keySize) {
|
||||
return window.crypto.subtle.importKey(
|
||||
"jwk",
|
||||
{
|
||||
kty: 'oct',
|
||||
k: k,
|
||||
alg: AesGcm.getAlgName(keySize),
|
||||
ext: true,
|
||||
}, {
|
||||
name: 'AES-GCM',
|
||||
},
|
||||
true,
|
||||
["encrypt", "decrypt"]
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Performs AES-GCM encryption.
|
||||
* @param {!CryptoKey} key The key that is used for encryption
|
||||
* @param {!ArrayBuffer} aad The additional authentication data
|
||||
* @param {!ArrayBuffer} msg The message to be encrypted
|
||||
* @param {!ArrayBuffer} iv The initialization vector
|
||||
* @param {number} tagSize The tag size in bits
|
||||
*
|
||||
* @return {!Promise} A Promise containing the ciphetext
|
||||
*/
|
||||
AesGcm.encrypt = function(key, aad, msg, iv, tagSize) {
|
||||
var alg = {
|
||||
name: 'AES-GCM',
|
||||
iv: iv,
|
||||
tagLength: tagSize
|
||||
};
|
||||
if (aad.byteLength > 0) {
|
||||
alg.additionalData = aad;
|
||||
}
|
||||
return window.crypto.subtle.encrypt(alg, key, msg);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tests AES-GCM encryption.
|
||||
*
|
||||
* @return {!Promise}
|
||||
*/
|
||||
AesGcm.testEncrypt = function() {
|
||||
var tc = this;
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
AesGcm.importKey(tc.k, tc.keySize).then(function(key){
|
||||
AesGcm.encrypt(key, tc.aad, tc.msg, tc.iv, tc.tagSize)
|
||||
.then(function(ct){
|
||||
// Fail if the iv is empty and the encryption still succeeds
|
||||
if (tc.ivSize == 0) {
|
||||
reject('Failed on test case ' + tc.id + ': 0-length iv should not be accepted');
|
||||
} else {
|
||||
var obtainedCt = TestUtil.arrayBufferToHex(ct);
|
||||
var expectedCt = tc.ct+tc.tag;
|
||||
if ((tc.result == 'valid' && obtainedCt != expectedCt) ||
|
||||
(tc.result == 'invalid' && obtainedCt == expectedCt)) {
|
||||
reject('Failed on test case ' + tc.id);
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
}).catch(function(err){
|
||||
// don't expect any exception during encryption
|
||||
reject('Unexpected exception on test case ' + tc.id + ': ' + err);
|
||||
});
|
||||
}).catch(function(err){
|
||||
reject('Failed to import key in test case ' + tc.id + ': ' + err);
|
||||
});
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tests AES-GCM encryption implementation with test vectors from files
|
||||
*
|
||||
* @return {!Promise}
|
||||
*/
|
||||
function testAesGcmVectors() {
|
||||
var tv = TestUtil.readJsonTestVectorsFromFile(AES_GCM_VECTOR_FILE);
|
||||
var testCase = new goog.testing.TestCase();
|
||||
testCase.promiseTimeout = 2*1000;
|
||||
|
||||
for (var i = 0; i < tv['testGroups'].length; i++) {
|
||||
var tg = tv['testGroups'][i];
|
||||
var keySizeBit = parseInt(tg['keySize']);
|
||||
var tagSizeBit = parseInt(tg['tagSize']);
|
||||
var ivSizeBit = parseInt(tg['ivSize']);
|
||||
// Only run the tests if the key size is supported
|
||||
if (SUPPORTED['aesgcm-key-size'].indexOf(keySizeBit) == -1) {
|
||||
continue;
|
||||
}
|
||||
// MS Edge only supports IVs of size 96
|
||||
if (goog.userAgent.product.EDGE && ivSizeBit != 96) {
|
||||
continue;
|
||||
}
|
||||
for (var j = 0; j < tg['tests'].length; j++){
|
||||
var tc = tg['tests'][j];
|
||||
// MS Edge doesn't accept 0-length message
|
||||
if (goog.userAgent.product.EDGE && tc['msg'].length == 0) {
|
||||
continue;
|
||||
}
|
||||
var k = BigInteger.fromHex(tc['key']).toBase64Url(keySizeBit/8);
|
||||
var aad = TestUtil.hexToArrayBuffer(tc['aad']);
|
||||
var msg = TestUtil.hexToArrayBuffer(tc['msg']);
|
||||
var iv;
|
||||
if (ivSizeBit == 0) {
|
||||
iv = new Uint8Array([]).buffer;
|
||||
} else {
|
||||
iv = BigInteger.fromHex(tc['iv']).toArrayBuffer(ivSizeBit/8);
|
||||
}
|
||||
var ct = tc['ct'];
|
||||
var tag = tc['tag'];
|
||||
var result = tc['result'];
|
||||
var tcId = tc['tcId'];
|
||||
var test = new AesGcmTestCase(tcId, k, keySizeBit, tag, tagSizeBit,
|
||||
iv, ivSizeBit, aad, msg, ct, result);
|
||||
testCase.addNewTest('Test ' + tcId, AesGcm.testEncrypt, test);
|
||||
}
|
||||
}
|
||||
|
||||
return testCase.runTestsReturningPromise().then(TestUtil.checkTestCaseResult);
|
||||
}
|
||||
Reference in New Issue
Block a user