Files
wycheproof/javascript/webcryptoapi/testcases/EcdhTest.js
T
bleichen 0fef006165 Refactoring ECDH tests:
Switching to test vector files that contain vectors for one curve only.
This covers more test cases, allows to skip test vectors for curves not
supported by a library.
ecdh_test.json contains tests for multiple curves, but is somewhat limited
in coverage.

Sanity checks for using expected files are now based on the JSON schema.
I.e. if the expected and actual JSON schema of the test vector file match,
then the test should pass, otherwise there is a problem with the test setup.

Removing tests covered by test vectors from EcdhTest.java.

Adding timing test for ECDH:
The tests require to run on a system without much noise:
E.g. running a regular unit test is too noisy.
The confidence level is too low to fail the test:
ECDH timing test:secp256r1
Timing for point 0 avg: 4062844.9155273438 std dev: 452064.39579909603 cv:0.11126794283271814
Timing for point 1 avg: 4077776.20703125 std dev: 457662.5685428607 cv:0.1122333706675025
Point 0 multiplication is faster: 1152

Running the same test locally adds less noise.
The ratio avg / std dev is 10 times smaller.
Here the test run shows that time of a point multiplication depends on the
point that is multiplied.

ECDH timing test:secp256r1
Timing for point 0 avg: 1611269.970703125 std dev: 30329.312528547747 cv:0.018823234516877802
Timing for point 1 avg: 1618931.3715820312 std dev: 29267.753517245954 cv:0.01807843990857086
Point 0 multiplication is faster: 1655

NOKEYCHECK=True
PiperOrigin-RevId: 232286329
GitOrigin-RevId: 1bbbd6422c8fd64f3d5ea2fc3ecb94c6bd7bc171
2019-11-25 17:38:50 -08:00

191 lines
5.3 KiB
JavaScript

/**
* @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 ECDH implementations of Web Crypto API.
*/
goog.provide('wycheproof.webcryptoapi.ECDH');
goog.require('goog.testing.TestCase');
goog.require('goog.testing.asserts');
goog.require('goog.testing.jsunit');
goog.require('wycheproof.BigInteger');
goog.require('wycheproof.TestUtil');
goog.require('wycheproof.webcryptoapi.RsaUtil');
var TestUtil = wycheproof.TestUtil;
var RsaUtil = wycheproof.webcryptoapi.RsaUtil;
var BigInteger = wycheproof.BigInteger;
// Test vector files
var ECDH_VECTOR_FILE = '../../testvectors/ecdh_webcrypto_test.json';
/** ECDH wrapper */
var Ecdh = function() {};
/**
* Generates an ECDH key pair using the given curve name.
* @param {!string} curveName The curve name
*
* @return {!Promise}
*/
Ecdh.generateKey = function(curveName) {
return window.crypto.subtle.generateKey(
{
name: 'ECDH',
namedCurve: curveName,
},
true,
['deriveKey', 'deriveBits']
);
};
/**
* Imports a ECDH key from the given key data.
* @param {!JSONObject} keyData The key data in JWK format
* @param {!Array<string>} usages The usages of the key
*
* @return {!Promise}
*/
Ecdh.importKey = function(keyData, usages) {
return window.crypto.subtle.importKey(
"jwk",
keyData,
{
name: 'ECDH',
namedCurve: keyData['crv'],
},
true,
usages
);
};
/**
* Exports the given ECDSA key as a JSON object.
* @param {!CryptoKey} key The ECDH key to be exported
*
* @return {!Promise}
*/
Ecdh.exportKey = function(key) {
return window.crypto.subtle.exportKey("jwk", key);
};
/**
* Derives bits from a public key and a private key.
* @param {!CryptoKey} pubKey The public key
* @param {!CryptoKey} privKey The private key
* @param {number} bitLen The number of bits to be derived
*
* @return {!Promise}
*/
Ecdh.deriveBits = function(pubKey, privKey, bitLen) {
return window.crypto.subtle.deriveBits(
{
name: 'ECDH',
public: pubKey,
},
privKey,
bitLen
);
};
/**
* Tests ECDH key derivation. The test case's parameters are passed
* to 'this' variable of the function.
*
* @return {!Promise}
*/
Ecdh.testKeyDerivation = function() {
tc = this;
var sk, pk;
var promise = new Promise(function(resolve, reject){
Ecdh.importKey(tc.privKeyData, ['deriveBits']).then(function(key){
sk = key;
Ecdh.importKey(tc.pubKeyData, []).then(function(key){
pk = key;
Ecdh.deriveBits(pk, sk, tc.sharedKeyLen).then(function(sharedKey){
if (tc.result == 'invalid') {
reject('Failed on test case ' + tc.id);
}
var hexSharedKey = TestUtil.arrayBufferToHex(sharedKey);
if (hexSharedKey != tc.sharedKey) {
reject('Failed on test case ' + tc.id);
}
resolve();
}).catch(function(err) {
if (tc.result == 'valid') {
reject('Unexpected exception on test case ' + tc.id + ": " + err);
}
resolve();
});
}).catch(function(err) {
if (tc.result == 'valid') {
reject('Failed to import public key: ' + err);
}
resolve();
});
}).catch(function(err) {
// Allow skipping P-256K curve since this curve is not yet supported
// by most implementations.
if (tc.privKeyData['crv'] == 'P-256K') {
resolve();
}
reject('Failed to import private key ' +tc.id + ":" + err);
});
});
return promise;
};
/**
* Parameters of a ECDH key derivation test.
* @param {!number} id Test case's id
* @param {!JSONObject} privKeyData The private key's data in JWK format
* @param {!JSONObject} pubKeyData The public key's data in JWK format
* @param {!string} sharedKey The expected shared key
* @param {!string} result The expected result of the test case
*/
var EcdhTestCase = function(id, privKeyData, pubKeyData, sharedKey, result) {
this.id = id;
this.privKeyData = privKeyData;
this.pubKeyData = pubKeyData;
this.sharedKey = sharedKey;
this.sharedKeyLen = sharedKey.length/2*8;
this.result = result;
};
/**
* Tests ECDH key derivation with a number of test vectors.
*
* @return {!Promise}
*/
function testEcdhVectors() {
var tv = TestUtil.readJsonTestVectorsFromFile(ECDH_VECTOR_FILE);
var testCase = new goog.testing.TestCase();
for (var i = 0; i < tv['testGroups'].length; i++) {
tg = tv['testGroups'][i];
for (var j = 0; j < tg['tests'].length; j++) {
tc = tg['tests'][j];
var test = new EcdhTestCase(tc['tcId'], tc['private'], tc['public'],
tc['shared'], tc['result']);
testCase.addNewTest(tc['tcId'], Ecdh.testKeyDerivation, test);
}
}
return testCase.runTestsReturningPromise().then(TestUtil.checkTestCaseResult);
}