Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / test / data / extensions / api_test / platform_keys / basic.js
blob8427717ae00cb391a985ead5fa2f0eafaef160c6
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 testSelectCA1Certs() {
232 assertCertsSelected({interactive: false, request: requestCA1()},
233 [data.client_1]);
236 function testSelectAllReturnsNoCerts() {
237 assertCertsSelected({interactive: false, request: requestAll},
238 [] /* no certs selected */);
241 function testSelectAllReturnsClient1() {
242 assertCertsSelected({interactive: false, request: requestAll},
243 [data.client_1]);
246 function testInteractiveSelectNoCerts() {
247 assertCertsSelected({interactive: true, request: requestAll},
248 [] /* no certs selected */);
251 function testInteractiveSelectClient1() {
252 assertCertsSelected({interactive: true, request: requestAll},
253 [data.client_1]);
256 function testInteractiveSelectClient2() {
257 var expectedCerts = [];
258 if (systemTokenEnabled)
259 expectedCerts.push(data.client_2);
260 assertCertsSelected({interactive: true, request: requestAll}, expectedCerts);
263 function testMatchResultCA1() {
264 chrome.platformKeys.selectClientCertificates(
265 {interactive: false, request: requestCA1()},
266 callbackPass(function(matches) {
267 var expectedAlgorithm = {
268 modulusLength: 2048,
269 name: "RSASSA-PKCS1-v1_5",
270 publicExponent: new Uint8Array([0x01, 0x00, 0x01])
272 var actualAlgorithm = matches[0].keyAlgorithm;
273 assertEq(
274 expectedAlgorithm, actualAlgorithm,
275 'Member algorithm of Match does not equal the expected algorithm');
276 }));
279 function testMatchResultECDSA() {
280 var requestECDSA = {
281 certificateTypes: ['ecdsaSign'],
282 certificateAuthorities: []
284 chrome.platformKeys.selectClientCertificates(
285 {interactive: false, request: requestECDSA},
286 callbackPass(function(matches) {
287 assertEq(0, matches.length, 'No matches expected.');
288 }));
291 function testMatchResultRSA() {
292 var requestRSA = {
293 certificateTypes: ['rsaSign'],
294 certificateAuthorities: []
296 chrome.platformKeys.selectClientCertificates(
297 {interactive: false, request: requestRSA},
298 callbackPass(function(matches) {
299 var expectedAlgorithm = {
300 modulusLength: 2048,
301 name: "RSASSA-PKCS1-v1_5",
302 publicExponent: new Uint8Array([0x01, 0x00, 0x01])
304 var actualAlgorithm = matches[0].keyAlgorithm;
305 assertEq(
306 expectedAlgorithm, actualAlgorithm,
307 'Member algorithm of Match does not equal the expected algorithm');
308 }));
311 function testGetKeyPairMissingAlgorithName() {
312 var keyParams = {
313 // This is missing the algorithm name.
314 hash: {name: 'SHA-1'}
316 try {
317 chrome.platformKeys.getKeyPair(
318 data.client_1.buffer, keyParams, function(error) {
319 fail('getKeyPair call was expected to fail.');
321 fail('getKeyPair did not throw error');
322 } catch (e) {
323 assertEq('Algorithm: name: Missing or not a String', e.message);
324 succeed();
328 function testGetKeyPairRejectsRSAPSS() {
329 var keyParams = {
330 name: 'RSA-PSS',
331 hash: {name: 'SHA-1'}
333 chrome.platformKeys.getKeyPair(
334 data.client_1.buffer, keyParams,
335 callbackFail(
336 'The requested Algorithm is not permitted by the certificate.'));
339 function testGetKeyPair() {
340 var keyParams = {
341 // Algorithm names are case-insensitive.
342 name: 'RSASSA-Pkcs1-V1_5',
343 hash: {name: 'sha-1'}
345 chrome.platformKeys.getKeyPair(
346 data.client_1.buffer, keyParams,
347 callbackPass(function(publicKey, privateKey) {
348 var expectedAlgorithm = {
349 modulusLength: 2048,
350 name: "RSASSA-PKCS1-v1_5",
351 publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
352 hash: {name: 'SHA-1'}
354 assertEq(expectedAlgorithm, publicKey.algorithm);
355 assertEq(expectedAlgorithm, privateKey.algorithm);
357 checkPublicKeyFormat(publicKey);
358 checkPrivateKeyFormat(privateKey);
360 chrome.platformKeys.subtleCrypto()
361 .exportKey('spki', publicKey)
362 .then(callbackPass(function(actualPublicKeySpki) {
363 assertTrue(
364 compareArrays(data.client_1_spki, actualPublicKeySpki) == 0,
365 'Match did not contain correct public key');
367 function(error) { fail("Export failed: " + error); });
368 }));
371 function testSignNoHash() {
372 var keyParams = {
373 // Algorithm names are case-insensitive.
374 name: 'RSASSA-PKCS1-V1_5',
375 hash: {name: 'NONE'}
377 var signParams = {
378 name: 'RSASSA-PKCS1-v1_5'
380 chrome.platformKeys.getKeyPair(
381 data.client_1.buffer, keyParams,
382 callbackPass(function(publicKey, privateKey) {
383 chrome.platformKeys.subtleCrypto()
384 .sign(signParams, privateKey, data.raw_data)
385 .then(callbackPass(function(signature) {
386 var actualSignature = new Uint8Array(signature);
387 assertTrue(compareArrays(data.signature_nohash_pkcs,
388 actualSignature) == 0,
389 'Incorrect signature');
390 }));
391 }));
394 function testSignSha1Client1() {
395 var keyParams = {
396 name: 'RSASSA-PKCS1-v1_5',
397 // Algorithm names are case-insensitive.
398 hash: {name: 'Sha-1'}
400 var signParams = {
401 // Algorithm names are case-insensitive.
402 name: 'RSASSA-Pkcs1-v1_5'
404 chrome.platformKeys.getKeyPair(
405 data.client_1.buffer, keyParams,
406 callbackPass(function(publicKey, privateKey) {
407 chrome.platformKeys.subtleCrypto()
408 .sign(signParams, privateKey, data.raw_data)
409 .then(callbackPass(function(signature) {
410 var actualSignature = new Uint8Array(signature);
411 assertTrue(
412 compareArrays(data.signature_sha1_pkcs, actualSignature) == 0,
413 'Incorrect signature');
414 }));
415 }));
418 // TODO(pneubeck): Test this by verifying that no private key is returned, once
419 // that's implemented.
420 function testSignFails(cert) {
421 var keyParams = {
422 name: 'RSASSA-PKCS1-v1_5',
423 hash: {name: 'SHA-1'}
425 var signParams = {
426 name: 'RSASSA-PKCS1-v1_5'
428 chrome.platformKeys.getKeyPair(
429 cert.buffer, keyParams, callbackPass(function(publicKey, privateKey) {
430 chrome.platformKeys.subtleCrypto()
431 .sign(signParams, privateKey, data.raw_data)
432 .then(function(signature) { fail('sign was expected to fail.'); },
433 callbackPass(function(error) {
434 assertTrue(error instanceof Error);
435 assertEq(
436 'The operation failed for an operation-specific reason',
437 error.message);
438 }));
439 }));
442 function testSignClient1Fails() {
443 testSignFails(data.client_1);
446 function testSignClient2Fails() {
447 testSignFails(data.client_2);
450 function testBackgroundNoninteractiveSelect() {
451 var details = {interactive: false, request: requestAll};
453 chrome.runtime.getBackgroundPage(callbackPass(function(bp) {
454 bp.chrome.platformKeys.selectClientCertificates(
455 details, callbackPass(function(actualMatches) {
456 assertTrue(!bp.chrome.runtime.lastError);
457 var expectedCount = systemTokenEnabled ? 2 : 1;
458 assertEq(expectedCount, actualMatches.length);
459 }));
460 }));
463 function testBackgroundInteractiveSelect() {
464 var details = {interactive: true, request: requestAll};
466 chrome.runtime.getBackgroundPage(callbackPass(function(bp) {
467 bp.chrome.platformKeys.selectClientCertificates(
468 // callbackPass checks chrome.runtime.lastError and not the error of
469 // the background page.
470 details, callbackPass(function(actualMatches) {
471 assertEq(bp.chrome.runtime.lastError.message,
472 'Interactive calls must happen in the context of a ' +
473 'browser tab or a window.');
474 assertEq([], actualMatches);
475 }));
476 }));
479 function testVerifyTrusted() {
480 var details = {
481 serverCertificateChain: [data.trusted_l1_leaf_cert.buffer],
482 hostname: "l1_leaf"
484 chrome.platformKeys.verifyTLSServerCertificate(
485 details, callbackPass(function(result) {
486 assertTrue(result.trusted);
487 assertEq([], result.debug_errors);
488 }));
491 function testVerifyTrustedChain() {
492 var details = {
493 serverCertificateChain:
494 [data.trusted_l2_leaf_cert.buffer, data.trusted_l1_interm_cert.buffer],
495 hostname: "l2_leaf"
497 chrome.platformKeys.verifyTLSServerCertificate(
498 details, callbackPass(function(result) {
499 assertTrue(result.trusted);
500 assertEq([], result.debug_errors);
501 }));
504 function testVerifyCommonNameInvalid() {
505 var details = {
506 serverCertificateChain:
507 [data.trusted_l2_leaf_cert.buffer, data.trusted_l1_interm_cert.buffer],
508 // Use any hostname not matching the common name 'l2_leaf' of the cert.
509 hostname: "abc.example"
511 chrome.platformKeys.verifyTLSServerCertificate(
512 details, callbackPass(function(result) {
513 assertFalse(result.trusted);
514 assertEq(["COMMON_NAME_INVALID"], result.debug_errors);
515 }));
518 function testVerifyUntrusted() {
519 var details = {
520 serverCertificateChain: [data.client_1.buffer],
521 hostname: "127.0.0.1"
523 chrome.platformKeys.verifyTLSServerCertificate(
524 details, callbackPass(function(result) {
525 assertFalse(result.trusted);
526 assertEq(["COMMON_NAME_INVALID", "AUTHORITY_INVALID"],
527 result.debug_errors);
528 }));
531 var testSuites = {
532 // On interactive selectClientCertificates calls, the simulated user does not
533 // select any cert.
534 basicTests: function() {
535 var tests = [
536 testStaticMethods,
538 // Interactively select client_1 and client_2 to grant permissions for
539 // these certificates.
540 testInteractiveSelectClient1,
541 testInteractiveSelectClient2,
543 // In non-interactive calls both certs must be returned now.
544 testSelectAllCerts,
546 testBackgroundNoninteractiveSelect,
547 testBackgroundInteractiveSelect,
548 testSelectCA1Certs,
549 testInteractiveSelectNoCerts,
550 testMatchResultCA1,
551 testMatchResultECDSA,
552 testMatchResultRSA,
553 testGetKeyPairMissingAlgorithName,
554 testGetKeyPairRejectsRSAPSS,
555 testGetKeyPair,
556 testSignNoHash,
557 testSignSha1Client1,
558 testVerifyTrusted,
559 testVerifyTrustedChain,
560 testVerifyCommonNameInvalid,
561 testVerifyUntrusted,
564 chrome.test.runTests(tests);
567 // On interactive selectClientCertificates calls, the simulated user selects
568 // client_1, if matching.
569 permissionTests: function() {
570 var tests = [
571 // Without permissions both sign attempts fail.
572 testSignClient1Fails,
573 testSignClient2Fails,
575 // Without permissions, non-interactive select calls return no certs.
576 testSelectAllReturnsNoCerts,
578 testInteractiveSelectClient1,
579 // Now the permission for client_1 is granted.
581 // Verify that signing with client_1 is possible and with client_2 still
582 // fails.
583 testSignSha1Client1,
584 testSignClient2Fails,
586 // Verify that client_1 can still be selected interactively.
587 testInteractiveSelectClient1,
589 // Verify that client_1 but not client_2 is selected in non-interactive
590 // calls.
591 testSelectAllReturnsClient1,
594 chrome.test.runTests(tests);
597 managedProfile: function() {
598 var tests = [
599 // If the profile is managed, the user cannot grant permissions for any
600 // certificates.
601 testInteractiveSelectNoCerts
603 chrome.test.runTests(tests);
606 corporateKeyWithoutPermissionTests: function() {
607 var tests = [
608 // Directly trying to sign must fail
609 testSignClient1Fails,
611 // Interactively selecting must not show any cert to the user.
612 testInteractiveSelectNoCerts,
614 chrome.test.runTests(tests);
617 corporateKeyWithPermissionTests: function() {
618 var tests = [
619 // The extension has non-interactive access to all corporate keys, even
620 // without previous additional consent of the user.
621 testSignSha1Client1,
623 // Interactively selecting for client_1 will work as well.
624 testInteractiveSelectClient1,
626 chrome.test.runTests(tests);
629 policyDoesGrantAccessToNonCorporateKey: function() {
630 // The permission from policy must not affect usage of non-corproate keys.
631 var tests = [
632 // Attempts to sign must fail.
633 testSignClient1Fails,
635 // Interactive selection must not prompt the user and not return any
636 // certificate.
637 testInteractiveSelectNoCerts,
639 chrome.test.runTests(tests);
644 setUp(testSuites[selectedTestSuite]);