aboutsummaryrefslogtreecommitdiff
path: root/javascript/webcryptoapi/testcases/EcdhTest.js
blob: 3fddfc56f5d2a3522b4a73f12e5343bdb9d25297 (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
/**
 * @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);
}