Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / test / data / extensions / api_test / platform_keys / basic.js
blob5e1eccba7034db7c37eeb5783f72fc9da5c91f0f
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 'use strict';
7 var systemTokenEnabled = (location.search.indexOf("systemTokenEnabled") != -1);
8 var selectedTestSuite = location.hash.slice(1);
9 console.log('[SELECTED TEST SUITE] ' + selectedTestSuite +
10 ', systemTokenEnable ' + systemTokenEnabled);
12 var assertEq = chrome.test.assertEq;
13 var assertTrue = chrome.test.assertTrue;
14 var assertFalse = chrome.test.assertFalse;
15 var fail = chrome.test.fail;
16 var succeed = chrome.test.succeed;
17 var callbackPass = chrome.test.callbackPass;
18 var callbackFail= chrome.test.callbackFail;
20 // Each value is the path to a file in this extension's folder that will be
21 // loaded and replaced by a Uint8Array in the setUp() function below.
22 var data = {
23 // X.509 certificate in DER encoding issued by 'root.pem' which is set to be
24 // trusted by the test setup.
25 // Generated by create_test_certs.sh .
26 trusted_l1_leaf_cert: 'l1_leaf.der',
28 // X.509 intermediate CA certificate in DER encoding issued by 'root.pem'
29 // which is set to be trusted by the test setup.
30 // Generated by create_test_certs.sh .
31 trusted_l1_interm_cert: 'l1_interm.der',
33 // X.509 certificate in DER encoding issued by 'l1_interm'.
34 // Generated by create_test_certs.sh .
35 trusted_l2_leaf_cert: 'l2_leaf.der',
37 // X.509 client certificate in DER encoding.
38 // Algorithm in SPKI: rsaEncryption.
39 // openssl x509 -in net/data/ssl/certificates/client_1.pem -outform DER -out
40 // client_1.der
41 client_1: 'client_1.der',
43 // X.509 client certificate in DER encoding.
44 // Algorithm in SPKI: rsaEncryption.
45 // openssl x509 -in net/data/ssl/certificates/client_2.pem -outform DER -out
46 // client_2.der
47 client_2: 'client_2.der',
49 // The public key of client_1 as Subject Public Key Info in DER encoding.
50 // openssl rsa -in net/data/ssl/certificates/client_1.key -inform PEM -out
51 // pubkey.der -pubout -outform DER
52 client_1_spki: 'client_1_spki.der',
54 // The distinguished name of the CA that issued client_1 in DER encoding.
55 // openssl asn1parse -in client_1.der -inform DER -strparse 32 -out
56 // client_1_issuer_dn.der
57 client_1_issuer_dn: 'client_1_issuer_dn.der',
59 // echo -n "hello world" > data
60 raw_data: 'data',
62 // openssl rsautl -inkey net/data/ssl/certificates/client_1.key -sign -in
63 // data -pkcs -out signature_nohash_pkcs
64 signature_nohash_pkcs: 'signature_nohash_pkcs',
66 // openssl dgst -sha1 -sign net/data/ssl/certificates/client_1.key
67 // -out signature_sha1_pkcs data
68 signature_sha1_pkcs: 'signature_sha1_pkcs',
71 // Reads the binary file at |path| and passes it as a Uin8Array to |callback|.
72 function readFile(path, callback) {
73 var oReq = new XMLHttpRequest();
74 oReq.responseType = "arraybuffer";
75 oReq.open("GET", path, true /* asynchronous */);
76 oReq.onload = function() {
77 var arrayBuffer = oReq.response;
78 if (arrayBuffer) {
79 callback(new Uint8Array(arrayBuffer));
80 } else {
81 callback(null);
84 oReq.send(null);
87 // For each key in dictionary, replaces the path dictionary[key] by the content
88 // of the resource located at that path stored in a Uint8Array.
89 function readData(dictionary, callback) {
90 var keys = Object.keys(dictionary);
91 function recurse(index) {
92 if (index >= keys.length) {
93 callback();
94 return;
96 var key = keys[index];
97 var path = dictionary[key];
98 readFile(path, function(array) {
99 assertTrue(!!array);
100 dictionary[key] = array;
101 recurse(index + 1);
105 recurse(0);
108 function setUp(callback) {
109 readData(data, callback);
112 // Some array comparison. Note: not lexicographical!
113 function compareArrays(array1, array2) {
114 if (array1.length < array2.length)
115 return -1;
116 if (array1.length > array2.length)
117 return 1;
118 for (var i = 0; i < array1.length; i++) {
119 if (array1[i] < array2[i])
120 return -1;
121 if (array1[i] > array2[i])
122 return 1;
124 return 0;
128 * @param {ArrayBufferView[]} certs
129 * @return {ArrayBufferView[]} |certs| sorted in some order.
131 function sortCerts(certs) {
132 return certs.sort(compareArrays);
135 function assertCertsSelected(details, expectedCerts, callback) {
136 chrome.platformKeys.selectClientCertificates(
137 details, callbackPass(function(actualMatches) {
138 assertEq(expectedCerts.length, actualMatches.length,
139 'Number of stored certs not as expected');
140 if (expectedCerts.length == actualMatches.length) {
141 var actualCerts = actualMatches.map(function(match) {
142 return new Uint8Array(match.certificate);
144 actualCerts = sortCerts(actualCerts);
145 expectedCerts = sortCerts(expectedCerts);
146 for (var i = 0; i < expectedCerts.length; i++) {
147 assertEq(expectedCerts[i], actualCerts[i],
148 'Certs at index ' + i + ' differ');
151 if (callback)
152 callback();
153 }));
156 function checkAlgorithmIsCopiedOnRead(key) {
157 var algorithm = key.algorithm;
158 var originalAlgorithm = {
159 name: algorithm.name,
160 modulusLength: algorithm.modulusLength,
161 publicExponent: algorithm.publicExponent,
162 hash: {name: algorithm.hash.name}
164 var originalModulusLength = algorithm.modulusLength;
165 algorithm.hash.name = null;
166 algorithm.hash = null;
167 algorithm.name = null;
168 algorithm.modulusLength = null;
169 algorithm.publicExponent = null;
170 assertEq(originalAlgorithm, key.algorithm);
173 function checkPropertyIsReadOnly(object, key) {
174 var original = object[key];
175 try {
176 object[key] = {};
177 fail('Expected the property ' + key +
178 ' to be read-only and an exception to be thrown');
179 } catch (error) {
180 assertEq(original, object[key]);
184 function checkPrivateKeyFormat(privateKey) {
185 assertEq('private', privateKey.type);
186 assertEq(false, privateKey.extractable);
187 checkPropertyIsReadOnly(privateKey, 'algorithm');
188 checkAlgorithmIsCopiedOnRead(privateKey);
191 function checkPublicKeyFormat(publicKey) {
192 assertEq('public', publicKey.type);
193 assertEq(true, publicKey.extractable);
194 checkPropertyIsReadOnly(publicKey, 'algorithm');
195 checkAlgorithmIsCopiedOnRead(publicKey);
198 function testStaticMethods() {
199 assertTrue(!!chrome.platformKeys, "No platformKeys namespace.");
200 assertTrue(!!chrome.platformKeys.selectClientCertificates,
201 "No selectClientCertificates function.");
202 assertTrue(!!chrome.platformKeys.getKeyPair, "No getKeyPair method.");
203 assertTrue(!!chrome.platformKeys.subtleCrypto, "No subtleCrypto getter.");
204 assertTrue(!!chrome.platformKeys.subtleCrypto(), "No subtleCrypto object.");
205 assertTrue(!!chrome.platformKeys.subtleCrypto().sign, "No sign method.");
206 assertTrue(!!chrome.platformKeys.subtleCrypto().exportKey,
207 "No exportKey method.");
208 succeed();
211 var requestAll = {
212 certificateTypes: [],
213 certificateAuthorities: []
216 // Depends on |data|, thus it cannot be created immediately.
217 function requestCA1() {
218 return {
219 certificateTypes: [],
220 certificateAuthorities: [data.client_1_issuer_dn.buffer]
224 function testSelectAllCerts() {
225 var expectedCerts = [data.client_1];
226 if (systemTokenEnabled)
227 expectedCerts.push(data.client_2);
228 assertCertsSelected({interactive: false, request: requestAll}, expectedCerts);
231 function testSelectWithInputClientCerts() {
232 var expectedCerts = [];
233 if (systemTokenEnabled)
234 expectedCerts.push(data.client_2);
235 assertCertsSelected(
237 interactive: false,
238 request: requestAll,
239 clientCerts: [data.client_2.buffer]
241 expectedCerts);
244 function testSelectCA1Certs() {
245 assertCertsSelected({interactive: false, request: requestCA1()},
246 [data.client_1]);
249 function testSelectAllReturnsNoCerts() {
250 assertCertsSelected({interactive: false, request: requestAll},
251 [] /* no certs selected */);
254 function testSelectAllReturnsClient1() {
255 assertCertsSelected({interactive: false, request: requestAll},
256 [data.client_1]);
259 function testInteractiveSelectNoCerts() {
260 assertCertsSelected({interactive: true, request: requestAll},
261 [] /* no certs selected */);
264 function testInteractiveSelectClient1() {
265 assertCertsSelected({interactive: true, request: requestAll},
266 [data.client_1]);
269 function testInteractiveSelectClient2() {
270 var expectedCerts = [];
271 if (systemTokenEnabled)
272 expectedCerts.push(data.client_2);
273 assertCertsSelected({interactive: true, request: requestAll}, expectedCerts);
276 function testMatchResultCA1() {
277 chrome.platformKeys.selectClientCertificates(
278 {interactive: false, request: requestCA1()},
279 callbackPass(function(matches) {
280 var expectedAlgorithm = {
281 modulusLength: 2048,
282 name: "RSASSA-PKCS1-v1_5",
283 publicExponent: new Uint8Array([0x01, 0x00, 0x01])
285 var actualAlgorithm = matches[0].keyAlgorithm;
286 assertEq(
287 expectedAlgorithm, actualAlgorithm,
288 'Member algorithm of Match does not equal the expected algorithm');
289 }));
292 function testMatchResultECDSA() {
293 var requestECDSA = {
294 certificateTypes: ['ecdsaSign'],
295 certificateAuthorities: []
297 chrome.platformKeys.selectClientCertificates(
298 {interactive: false, request: requestECDSA},
299 callbackPass(function(matches) {
300 assertEq(0, matches.length, 'No matches expected.');
301 }));
304 function testMatchResultRSA() {
305 var requestRSA = {
306 certificateTypes: ['rsaSign'],
307 certificateAuthorities: []
309 chrome.platformKeys.selectClientCertificates(
310 {interactive: false, request: requestRSA},
311 callbackPass(function(matches) {
312 var expectedAlgorithm = {
313 modulusLength: 2048,
314 name: "RSASSA-PKCS1-v1_5",
315 publicExponent: new Uint8Array([0x01, 0x00, 0x01])
317 var actualAlgorithm = matches[0].keyAlgorithm;
318 assertEq(
319 expectedAlgorithm, actualAlgorithm,
320 'Member algorithm of Match does not equal the expected algorithm');
321 }));
324 function testGetKeyPairMissingAlgorithName() {
325 var keyParams = {
326 // This is missing the algorithm name.
327 hash: {name: 'SHA-1'}
329 try {
330 chrome.platformKeys.getKeyPair(
331 data.client_1.buffer, keyParams, function(error) {
332 fail('getKeyPair call was expected to fail.');
334 fail('getKeyPair did not throw error');
335 } catch (e) {
336 assertEq('Algorithm: name: Missing or not a String', e.message);
337 succeed();
341 function testGetKeyPairRejectsRSAPSS() {
342 var keyParams = {
343 name: 'RSA-PSS',
344 hash: {name: 'SHA-1'}
346 chrome.platformKeys.getKeyPair(
347 data.client_1.buffer, keyParams,
348 callbackFail(
349 'The requested Algorithm is not permitted by the certificate.'));
352 function testGetKeyPair() {
353 var keyParams = {
354 // Algorithm names are case-insensitive.
355 name: 'RSASSA-Pkcs1-V1_5',
356 hash: {name: 'sha-1'}
358 chrome.platformKeys.getKeyPair(
359 data.client_1.buffer, keyParams,
360 callbackPass(function(publicKey, privateKey) {
361 var expectedAlgorithm = {
362 modulusLength: 2048,
363 name: "RSASSA-PKCS1-v1_5",
364 publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
365 hash: {name: 'SHA-1'}
367 assertEq(expectedAlgorithm, publicKey.algorithm);
368 assertEq(expectedAlgorithm, privateKey.algorithm);
370 checkPublicKeyFormat(publicKey);
371 checkPrivateKeyFormat(privateKey);
373 chrome.platformKeys.subtleCrypto()
374 .exportKey('spki', publicKey)
375 .then(callbackPass(function(actualPublicKeySpki) {
376 assertTrue(
377 compareArrays(data.client_1_spki, actualPublicKeySpki) == 0,
378 'Match did not contain correct public key');
380 function(error) { fail("Export failed: " + error); });
381 }));
384 function testSignNoHash() {
385 var keyParams = {
386 // Algorithm names are case-insensitive.
387 name: 'RSASSA-PKCS1-V1_5',
388 hash: {name: 'NONE'}
390 var signParams = {
391 name: 'RSASSA-PKCS1-v1_5'
393 chrome.platformKeys.getKeyPair(
394 data.client_1.buffer, keyParams,
395 callbackPass(function(publicKey, privateKey) {
396 chrome.platformKeys.subtleCrypto()
397 .sign(signParams, privateKey, data.raw_data)
398 .then(callbackPass(function(signature) {
399 var actualSignature = new Uint8Array(signature);
400 assertTrue(compareArrays(data.signature_nohash_pkcs,
401 actualSignature) == 0,
402 'Incorrect signature');
403 }));
404 }));
407 function testSignSha1Client1() {
408 var keyParams = {
409 name: 'RSASSA-PKCS1-v1_5',
410 // Algorithm names are case-insensitive.
411 hash: {name: 'Sha-1'}
413 var signParams = {
414 // Algorithm names are case-insensitive.
415 name: 'RSASSA-Pkcs1-v1_5'
417 chrome.platformKeys.getKeyPair(
418 data.client_1.buffer, keyParams,
419 callbackPass(function(publicKey, privateKey) {
420 chrome.platformKeys.subtleCrypto()
421 .sign(signParams, privateKey, data.raw_data)
422 .then(callbackPass(function(signature) {
423 var actualSignature = new Uint8Array(signature);
424 assertTrue(
425 compareArrays(data.signature_sha1_pkcs, actualSignature) == 0,
426 'Incorrect signature');
427 }));
428 }));
431 // TODO(pneubeck): Test this by verifying that no private key is returned, once
432 // that's implemented.
433 function testSignFails(cert) {
434 var keyParams = {
435 name: 'RSASSA-PKCS1-v1_5',
436 hash: {name: 'SHA-1'}
438 var signParams = {
439 name: 'RSASSA-PKCS1-v1_5'
441 chrome.platformKeys.getKeyPair(
442 cert.buffer, keyParams, callbackPass(function(publicKey, privateKey) {
443 chrome.platformKeys.subtleCrypto()
444 .sign(signParams, privateKey, data.raw_data)
445 .then(function(signature) { fail('sign was expected to fail.'); },
446 callbackPass(function(error) {
447 assertTrue(error instanceof Error);
448 assertEq(
449 'The operation failed for an operation-specific reason',
450 error.message);
451 }));
452 }));
455 function testSignClient1Fails() {
456 testSignFails(data.client_1);
459 function testSignClient2Fails() {
460 testSignFails(data.client_2);
463 function testBackgroundNoninteractiveSelect() {
464 var details = {interactive: false, request: requestAll};
466 chrome.runtime.getBackgroundPage(callbackPass(function(bp) {
467 bp.chrome.platformKeys.selectClientCertificates(
468 details, callbackPass(function(actualMatches) {
469 assertTrue(!bp.chrome.runtime.lastError);
470 var expectedCount = systemTokenEnabled ? 2 : 1;
471 assertEq(expectedCount, actualMatches.length);
472 }));
473 }));
476 function testBackgroundInteractiveSelect() {
477 var details = {interactive: true, request: requestAll};
479 chrome.runtime.getBackgroundPage(callbackPass(function(bp) {
480 bp.chrome.platformKeys.selectClientCertificates(
481 // callbackPass checks chrome.runtime.lastError and not the error of
482 // the background page.
483 details, callbackPass(function(actualMatches) {
484 assertEq(bp.chrome.runtime.lastError.message,
485 'Interactive calls must happen in the context of a ' +
486 'browser tab or a window.');
487 assertEq([], actualMatches);
488 }));
489 }));
492 function testVerifyTrusted() {
493 var details = {
494 serverCertificateChain: [data.trusted_l1_leaf_cert.buffer],
495 hostname: "l1_leaf"
497 chrome.platformKeys.verifyTLSServerCertificate(
498 details, callbackPass(function(result) {
499 assertTrue(result.trusted);
500 assertEq([], result.debug_errors);
501 }));
504 function testVerifyTrustedChain() {
505 var details = {
506 serverCertificateChain:
507 [data.trusted_l2_leaf_cert.buffer, data.trusted_l1_interm_cert.buffer],
508 hostname: "l2_leaf"
510 chrome.platformKeys.verifyTLSServerCertificate(
511 details, callbackPass(function(result) {
512 assertTrue(result.trusted);
513 assertEq([], result.debug_errors);
514 }));
517 function testVerifyCommonNameInvalid() {
518 var details = {
519 serverCertificateChain:
520 [data.trusted_l2_leaf_cert.buffer, data.trusted_l1_interm_cert.buffer],
521 // Use any hostname not matching the common name 'l2_leaf' of the cert.
522 hostname: "abc.example"
524 chrome.platformKeys.verifyTLSServerCertificate(
525 details, callbackPass(function(result) {
526 assertFalse(result.trusted);
527 assertEq(["COMMON_NAME_INVALID"], result.debug_errors);
528 }));
531 function testVerifyUntrusted() {
532 var details = {
533 serverCertificateChain: [data.client_1.buffer],
534 hostname: "127.0.0.1"
536 chrome.platformKeys.verifyTLSServerCertificate(
537 details, callbackPass(function(result) {
538 assertFalse(result.trusted);
539 assertEq(["COMMON_NAME_INVALID", "AUTHORITY_INVALID"],
540 result.debug_errors);
541 }));
544 var testSuites = {
545 // On interactive selectClientCertificates calls, the simulated user does not
546 // select any cert.
547 basicTests: function() {
548 var tests = [
549 testStaticMethods,
551 // Interactively select client_1 and client_2 to grant permissions for
552 // these certificates.
553 testInteractiveSelectClient1,
554 testInteractiveSelectClient2,
556 // In non-interactive calls both certs must be returned now.
557 testSelectAllCerts,
559 testBackgroundNoninteractiveSelect,
560 testBackgroundInteractiveSelect,
561 testSelectWithInputClientCerts,
562 testSelectCA1Certs,
563 testInteractiveSelectNoCerts,
564 testMatchResultCA1,
565 testMatchResultECDSA,
566 testMatchResultRSA,
567 testGetKeyPairMissingAlgorithName,
568 testGetKeyPairRejectsRSAPSS,
569 testGetKeyPair,
570 testSignNoHash,
571 testSignSha1Client1,
572 testVerifyTrusted,
573 testVerifyTrustedChain,
574 testVerifyCommonNameInvalid,
575 testVerifyUntrusted,
578 chrome.test.runTests(tests);
581 // On interactive selectClientCertificates calls, the simulated user selects
582 // client_1, if matching.
583 permissionTests: function() {
584 var tests = [
585 // Without permissions both sign attempts fail.
586 testSignClient1Fails,
587 testSignClient2Fails,
589 // Without permissions, non-interactive select calls return no certs.
590 testSelectAllReturnsNoCerts,
592 testInteractiveSelectClient1,
593 // Now the permission for client_1 is granted.
595 // Verify that signing with client_1 is possible and with client_2 still
596 // fails.
597 testSignSha1Client1,
598 testSignClient2Fails,
600 // Verify that client_1 can still be selected interactively.
601 testInteractiveSelectClient1,
603 // Verify that client_1 but not client_2 is selected in non-interactive
604 // calls.
605 testSelectAllReturnsClient1,
608 chrome.test.runTests(tests);
611 managedProfile: function() {
612 var tests = [
613 // If the profile is managed, the user cannot grant permissions for any
614 // certificates.
615 testInteractiveSelectNoCerts
617 chrome.test.runTests(tests);
620 corporateKeyWithoutPermissionTests: function() {
621 var tests = [
622 // Directly trying to sign must fail
623 testSignClient1Fails,
625 // Interactively selecting must not show any cert to the user.
626 testInteractiveSelectNoCerts,
628 chrome.test.runTests(tests);
631 corporateKeyWithPermissionTests: function() {
632 var tests = [
633 // The extension has non-interactive access to all corporate keys, even
634 // without previous additional consent of the user.
635 testSignSha1Client1,
637 // Interactively selecting for client_1 will work as well.
638 testInteractiveSelectClient1,
640 chrome.test.runTests(tests);
643 policyDoesGrantAccessToNonCorporateKey: function() {
644 // The permission from policy must not affect usage of non-corproate keys.
645 var tests = [
646 // Attempts to sign must fail.
647 testSignClient1Fails,
649 // Interactive selection must not prompt the user and not return any
650 // certificate.
651 testInteractiveSelectNoCerts,
653 chrome.test.runTests(tests);
658 setUp(testSuites[selectedTestSuite]);