+2019-09-16 Jiewen Tan <jiewen_tan@apple.com>
+
+ [WebAuthn] LocalAuthenticator tests are failing on internal bots
+ https://bugs.webkit.org/show_bug.cgi?id=201844
+ <rdar://problem/54278693>
+
+ Reviewed by Brent Fulgham.
+
+ This patch makes each test to generate their own credentials to avoid race conditions in Keychain
+ accesses when multiple test runners present.
+
+ * http/wpt/credential-management/credentialscontainer-store-basics.https.html:
+ * http/wpt/webauthn/idl.https.html:
+ These two tests are changed to use hid authenticator instead of local to avoid any future Keychain issues.
+ * http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html:
+ * http/wpt/webauthn/public-key-credential-create-failure-local.https.html:
+ * http/wpt/webauthn/public-key-credential-create-success-local.https.html:
+ * http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html:
+ * http/wpt/webauthn/public-key-credential-get-failure-local.https.html:
+ * http/wpt/webauthn/public-key-credential-get-success-local.https.html:
+ * http/wpt/webauthn/resources/util.js:
+ * platform/mac-wk2/TestExpectations:
+
2019-09-23 Youenn Fablet <youenn@apple.com>
Simplify UserMediaPermissionRequestManager management of UserMediaRequest
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
- const testES256PrivateKeyBase64 =
- "BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
- "PH76c0+WFOzZKslPyyFse4goGIW2R7k9VHLPEZl5nfnBgEVFh5zev+/xpHQIvuq6" +
- "RQ==";
- const testAttestationCertificateBase64 =
- "MIIB6jCCAZCgAwIBAgIGAWHAxcjvMAoGCCqGSM49BAMCMFMxJzAlBgNVBAMMHkJh" +
- "c2ljIEF0dGVzdGF0aW9uIFVzZXIgU3ViIENBMTETMBEGA1UECgwKQXBwbGUgSW5j" +
- "LjETMBEGA1UECAwKQ2FsaWZvcm5pYTAeFw0xODAyMjMwMzM3MjJaFw0xODAyMjQw" +
- "MzQ3MjJaMGoxIjAgBgNVBAMMGTAwMDA4MDEwLTAwMEE0OUEyMzBBMDIxM0ExGjAY" +
- "BgNVBAsMEUJBQSBDZXJ0aWZpY2F0aW9uMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMw" +
- "EQYDVQQIDApDYWxpZm9ybmlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvCje" +
- "Pzr6Sg76XMoHuGabPaG6zjpLFL8Zd8/74Hh5PcL2Zq+o+f7ENXX+7nEXXYt0S8Ux" +
- "5TIRw4hgbfxXQbWLEqM5MDcwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBPAw" +
- "FwYJKoZIhvdjZAgCBAowCKEGBAR0ZXN0MAoGCCqGSM49BAMCA0gAMEUCIAlK8A8I" +
- "k43TbvKuYGHZs1DTgpTwmKTBvIUw5bwgZuYnAiEAtuJjDLKbGNJAJFMi5deEBqno" +
- "pBTCqbfbDJccfyQpjnY=";
- const testAttestationIssuingCACertificateBase64 =
- "MIICIzCCAaigAwIBAgIIeNjhG9tnDGgwCgYIKoZIzj0EAwIwUzEnMCUGA1UEAwwe" +
- "QmFzaWMgQXR0ZXN0YXRpb24gVXNlciBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ" +
- "bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTE3MDQyMDAwNDIwMFoXDTMyMDMy" +
- "MjAwMDAwMFowUzEnMCUGA1UEAwweQmFzaWMgQXR0ZXN0YXRpb24gVXNlciBTdWIg" +
- "Q0ExMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMFkw" +
- "EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoSZ/1t9eBAEVp5a8PrXacmbGb8zNC1X3" +
- "StLI9YO6Y0CL7blHmSGmjGWTwD4Q+i0J2BY3+bPHTGRyA9jGB3MSbaNmMGQwEgYD" +
- "VR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBSD5aMhnrB0w/lhkP2XTiMQdqSj" +
- "8jAdBgNVHQ4EFgQU5mWf1DYLTXUdQ9xmOH/uqeNSD80wDgYDVR0PAQH/BAQDAgEG" +
- "MAoGCCqGSM49BAMCA2kAMGYCMQC3M360LLtJS60Z9q3vVjJxMgMcFQ1roGTUcKqv" +
- "W+4hJ4CeJjySXTgq6IEHn/yWab4CMQCm5NnK6SOSK+AqWum9lL87W3E6AA1f2TvJ" +
- "/hgok/34jr93nhS87tOQNdxDS8zyiqw=";
- const testRpId = "localhost";
- function asciiToUint8Array(str)
- {
- var chars = [];
- for (var i = 0; i < str.length; ++i)
- chars.push(str.charCodeAt(i));
- return new Uint8Array(chars);
- }
+ const testCreationMessageBase64 =
+ "AKMBZnBhY2tlZAJYxEbMf7lnnVWy25CS4cjZ5eHQK3WA8LSBLHcJYuHkj1rYQQAA" +
+ "AE74oBHzjApNFYAGFxEfntx9AEAoCK3O6P5OyXN6V/f+9nAga0NA2Cgp4V3mgSJ5" +
+ "jOHLMDrmxp/S0rbD+aihru1C0aAN3BkiM6GNy5nSlDVqOgTgpQECAyYgASFYIEFb" +
+ "he3RkNud6sgyraBGjlh1pzTlCZehQlL/b18HZ6WGIlggJgfUd/en9p5AIqMQbUni" +
+ "nEeXdFLkvW0/zV5BpEjjNxADo2NhbGcmY3NpZ1hHMEUCIQDKg+ZBmEBtf0lWq4Re" +
+ "dH4/i/LOYqOR4uR2NAj2zQmw9QIgbTXb4hvFbj4T27bv/rGrc+y+0puoYOBkBk9P" +
+ "mCewWlNjeDVjgVkCwjCCAr4wggGmoAMCAQICBHSG/cIwDQYJKoZIhvcNAQELBQAw" +
+ "LjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEw" +
+ "IBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG8xCzAJBgNVBAYTAlNF" +
+ "MRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0" +
+ "ZXN0YXRpb24xKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NTUwMDM4" +
+ "NDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASVXfOt9yR9MXXv/ZzE8xpOh466" +
+ "4YEJVmFQ+ziLLl9lJ79XQJqlgaUNCsUvGERcChNUihNTyKTlmnBOUjvATevto2ww" +
+ "ajAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuMTATBgsrBgEEAYLl" +
+ "HAIBAQQEAwIFIDAhBgsrBgEEAYLlHAEBBAQSBBD4oBHzjApNFYAGFxEfntx9MAwG" +
+ "A1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBADFcSIDmmlJ+OGaJvWn9Cqhv" +
+ "SeueToVFQVVvqtALOgCKHdwB+Wx29mg2GpHiMsgQp5xjB0ybbnpG6x212FxESJ+G" +
+ "inZD0ipchi7APwPlhIvjgH16zVX44a4e4hOsc6tLIOP71SaMsHuHgCcdH0vg5d2s" +
+ "c006WJe9TXO6fzV+ogjJnYpNKQLmCXoAXE3JBNwKGBIOCvfQDPyWmiiG5bGxYfPt" +
+ "y8Z3pnjX+1MDnM2hhr40ulMxlSNDnX/ZSnDyMGIbk8TOQmjTF02UO8auP8k3wt5D" +
+ "1rROIRU9+FCSX5WQYi68RuDrGMZB8P5+byoJqbKQdxn2LmE1oZAyohPAmLcoPO4=";
promise_test(async function(t) {
const options = {
},
user: {
name: "John Appleseed",
- id: asciiToUint8Array("123456"),
+ id: new Uint8Array(16),
displayName: "Appleseed",
},
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ challenge: new Uint8Array(16),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }]
}
};
// A mock attestation object
if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({
- local: {
- acceptAuthentication: true,
- acceptAttestation: true,
- privateKeyBase64: testES256PrivateKeyBase64,
- userCertificateBase64: testAttestationCertificateBase64,
- intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64
- }
- });
+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testCreationMessageBase64] } });
const credential = await navigator.credentials.create(options);
- return promise_rejects(t, "NotSupportedError",
- navigator.credentials.store(credential)).then(() => {
- if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
- });
+ return promise_rejects(t, "NotSupportedError", navigator.credentials.store(credential));
}, "navigator.credentials.store().");
</script>
<div id='log'></div>
<script>
-if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({
- local: {
- acceptAuthentication: true,
- acceptAttestation: true,
- privateKeyBase64: testES256PrivateKeyBase64,
- userCertificateBase64: testAttestationCertificateBase64,
- intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64
- }
- });
-
promise_test(async () => {
const idlURL = ["WebAuthN.idl"];
const idlArray = new IdlArray();
name: "localhost",
},
user: {
- name: "John Appleseed",
+ name: testUserhandleBase64,
id: Base64URL.parse(testUserhandleBase64),
displayName: "Appleseed",
},
pubKeyCredParams: [{ type: "public-key", alg: -7 }],
}
};
+ // A mock attestation object
+ if (window.testRunner)
+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testCreationMessageBase64] } });
createdCredential = await navigator.credentials.create(creationOptions);
-
const requestOptions = {
publicKey: {
challenge: Base64URL.parse("MTIzNDU2"),
}
};
+ if (window.testRunner)
+ testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testAssertionMessageBase64] } });
requestedCredential = await navigator.credentials.get(requestOptions);
idlArray.add_objects({"PublicKeyCredential": ["createdCredential"], "AuthenticatorAttestationResponse": ["createdCredential.response"], "AuthenticatorAssertionResponse": ["requestedCredential.response"]});
idlArray.test();
-
- if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
}, "Setup for WebAuthN API IDL tests.");
</script>
<script src="/resources/testharnessreport.js"></script>
<script src="./resources/util.js"></script>
<script>
- // Default mock configuration. Tests need to override if they need different configuration.
- if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false } });
+ (async function() {
+ const userhandleBase64 = generateUserhandleBase64();
+ const privateKeyBase64 = await generatePrivateKeyBase64();
+ const credentialID = await calculateCredentialID(privateKeyBase64);
+ // Default mock configuration. Tests need to override if they need different configuration.
+ if (window.testRunner)
+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false } });
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -35 }, { type: "public-key", alg: -257 }], // ES384, RS256
- timeout: 10
- }
- };
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
- }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator.");
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(testUserhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -35 }, { type: "public-key", alg: -257 }], // ES384, RS256
+ timeout: 10
+ }
+ };
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
+ }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator.");
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }],
- timeout: 10
- }
- };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }],
+ timeout: 10
+ }
+ };
if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
- });
- }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 2");
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+ if (window.testRunner)
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+ });
+ }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 2");
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- excludeCredentials: [
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["usb"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["internal"] }
- ],
- timeout: 10
- }
- };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ excludeCredentials: [
+ { type: "public-key", id: credentialID, transports: ["usb"] },
+ { type: "public-key", id: credentialID, transports: ["nfc"] },
+ { type: "public-key", id: credentialID, transports: ["ble"] },
+ { type: "public-key", id: credentialID, transports: ["internal"] }
+ ],
+ timeout: 10
+ }
+ };
if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
- });
- }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 3");
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+ if (window.testRunner)
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+ });
+ }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 3");
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- timeout: 10
- }
- };
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
- }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 4");
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(testUserhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ timeout: 10
+ }
+ };
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
+ }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 4");
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- timeout: 10
- }
- };
- if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: true, acceptAttestation: false } });
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
- }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 5");
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(testUserhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ timeout: 10
+ }
+ };
+ if (window.testRunner)
+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: true, acceptAttestation: false } });
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
+ }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 5");
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- timeout: 10
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ timeout: 10
+ }
+ };
+ if (window.testRunner) {
+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: true, acceptAttestation: false } });
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
}
- };
- if (window.testRunner) {
- testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: true, acceptAttestation: false } });
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- }
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
- if (window.testRunner)
- assert_false(testRunner.keyExistsInKeychain(testRpId, testUserhandleBase64));
- });
- }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 6");
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
+ if (window.testRunner)
+ assert_false(testRunner.keyExistsInKeychain(testRpId, userhandleBase64));
+ });
+ }, "PublicKeyCredential's [[create]] with silent failure in a mock local authenticator. 6");
+ })();
</script>
<script src="/resources/testharnessreport.js"></script>
<script src="./resources/util.js"></script>
<script>
- // Default mock configuration. Tests need to override if they need different configuration.
- if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
+ (async function() {
+ const userhandleBase64 = generateUserhandleBase64();
+ const privateKeyBase64 = await generatePrivateKeyBase64();
+ const credentialID = await calculateCredentialID(privateKeyBase64);
+ // Default mock configuration. Tests need to override if they need different configuration.
+ if (window.testRunner)
+ testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -35 }, { type: "public-key", alg: -257 }], // ES384, RS256
- }
- };
- return promiseRejects(t, "NotSupportedError", navigator.credentials.create(options), "The platform attached authenticator doesn't support any provided PublicKeyCredentialParameters.");
- }, "PublicKeyCredential's [[create]] with unsupported public key credential parameters in a mock local authenticator.");
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(testUserhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -35 }, { type: "public-key", alg: -257 }], // ES384, RS256
+ }
+ };
+ return promiseRejects(t, "NotSupportedError", navigator.credentials.create(options), "The platform attached authenticator doesn't support any provided PublicKeyCredentialParameters.");
+ }, "PublicKeyCredential's [[create]] with unsupported public key credential parameters in a mock local authenticator.");
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- excludeCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }]
- }
- };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.").then(() => {
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ excludeCredentials: [{ type: "public-key", id: credentialID }]
+ }
+ };
if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
- });
- }, "PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator.");
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.").then(() => {
+ if (window.testRunner)
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+ });
+ }, "PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator.");
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- excludeCredentials: [
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["usb"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["internal"] }
- ]
- }
- };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.").then(() => {
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ excludeCredentials: [
+ { type: "public-key", id: credentialID, transports: ["usb"] },
+ { type: "public-key", id: credentialID, transports: ["nfc"] },
+ { type: "public-key", id: credentialID, transports: ["ble"] },
+ { type: "public-key", id: credentialID, transports: ["internal"] }
+ ]
+ }
+ };
if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
- });
- }, "PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator. 2nd");
-
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }]
- }
- };
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Couldn't get user consent.");
- }, "PublicKeyCredential's [[create]] without user consent in a mock local authenticator.");
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.").then(() => {
+ if (window.testRunner)
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+ });
+ }, "PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator. 2nd");
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }]
- }
- };
- if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
- return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error.");
- }, "PublicKeyCredential's [[create]] without attestation in a mock local authenticator.");
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(testUserhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }]
+ }
+ };
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Couldn't get user consent.");
+ }, "PublicKeyCredential's [[create]] without user consent in a mock local authenticator.");
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }]
- }
- };
- if (window.testRunner) {
- testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- }
- return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error.").then(() => {
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: Base64URL.parse(testUserhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }]
+ }
+ };
if (window.testRunner)
- assert_false(testRunner.keyExistsInKeychain(testRpId, testUserhandleBase64));
- });
- }, "PublicKeyCredential's [[create]] deleting old credential in a mock local authenticator.");
+ testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
+ return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error.");
+ }, "PublicKeyCredential's [[create]] without attestation in a mock local authenticator.");
- promise_test(function(t) {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: asciiToUint8Array("123456"),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- timeout: 10,
- authenticatorSelection: { authenticatorAttachment: "cross-platform" }
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: userhandleBase64,
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }]
+ }
+ };
+ if (window.testRunner) {
+ testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
}
- };
+ return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Unknown internal error.").then(() => {
+ if (window.testRunner)
+ assert_false(testRunner.keyExistsInKeychain(testRpId, userhandleBase64));
+ });
+ }, "PublicKeyCredential's [[create]] deleting old credential in a mock local authenticator.");
- if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
- return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
- }, "PublicKeyCredential's [[create]] with timeout in a mock local authenticator.");
+ promise_test(function(t) {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: "John Appleseed",
+ id: asciiToUint8Array("123456"),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ timeout: 10,
+ authenticatorSelection: { authenticatorAttachment: "cross-platform" }
+ }
+ };
+
+ if (window.testRunner)
+ testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
+ }, "PublicKeyCredential's [[create]] with timeout in a mock local authenticator.");
+ })();
</script>
<script src="./resources/util.js"></script>
<script src="./resources/cbor.js"></script>
<script>
- // Default mock configuration. Tests need to override if they need different configuration.
- if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({
- local: {
- acceptAuthentication: true,
- acceptAttestation: true,
- privateKeyBase64: testES256PrivateKeyBase64,
- userCertificateBase64: testAttestationCertificateBase64,
- intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64
+ (async function() {
+ const userhandleBase64 = generateUserhandleBase64();
+ const privateKeyBase64 = await generatePrivateKeyBase64();
+ const credentialID = await calculateCredentialID(privateKeyBase64);
+ // Default mock configuration. Tests need to override if they need different configuration.
+ if (window.testRunner)
+ testRunner.setWebAuthenticationMockConfiguration({
+ local: {
+ acceptAuthentication: true,
+ acceptAttestation: true,
+ privateKeyBase64: privateKeyBase64,
+ userCertificateBase64: testAttestationCertificateBase64,
+ intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64
+ }
+ });
+
+ function checkResult(credential, isNoneAttestation = true)
+ {
+ // Check keychain
+ if (window.testRunner) {
+ assert_true(testRunner.keyExistsInKeychain(testRpId, userhandleBase64));
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
}
- });
-
- function checkResult(credential, isNoneAttestation = true)
- {
- // Check keychain
- if (window.testRunner) {
- assert_true(testRunner.keyExistsInKeychain(testRpId, testUserhandleBase64));
- testRunner.cleanUpKeychain(testRpId);
- }
- // Check respond
- assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testCredentialIdBase64));
- assert_equals(credential.type, 'public-key');
- assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testCredentialIdBase64));
- assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
- assert_not_own_property(credential.getClientExtensionResults(), "appid");
-
- // Check attestation
- const attestationObject = CBOR.decode(credential.response.attestationObject);
- if (isNoneAttestation)
- assert_equals(attestationObject.fmt, "none");
- else
- assert_equals(attestationObject.fmt, "Apple");
- // Check authData
- const authData = decodeAuthData(attestationObject.authData);
- assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
- assert_equals(authData.flags, 69);
- assert_equals(authData.counter, 0);
- assert_equals(bytesToHexString(authData.aaguid), "00000000000000000000000000000000");
- assert_array_equals(authData.credentialID, Base64URL.parse(testCredentialIdBase64));
- // Check self attestation
- assert_true(checkPublicKey(authData.publicKey));
- if (isNoneAttestation)
- assert_object_equals(attestationObject.attStmt, { });
- else {
- assert_equals(attestationObject.attStmt.alg, -7);
- assert_equals(attestationObject.attStmt.x5c.length, 2);
- assert_array_equals(attestationObject.attStmt.x5c[0], Base64URL.parse(testAttestationCertificateBase64));
- assert_array_equals(attestationObject.attStmt.x5c[1], Base64URL.parse(testAttestationIssuingCACertificateBase64));
-
- // Check signature
- let publicKeyData = new Uint8Array(65);
- publicKeyData[0] = 0x04;
- publicKeyData.set(authData.publicKey['-2'], 1);
- publicKeyData.set(authData.publicKey['-3'], 33);
- return crypto.subtle.importKey("raw", publicKeyData, {
- name: "ECDSA",
- namedCurve: "P-256"
- }, false, ['verify']).then(publicKey => {
- return crypto.subtle.verify({
+ // Check respond
+ assert_array_equals(Base64URL.parse(credential.id), credentialID);
+ assert_equals(credential.type, 'public-key');
+ assert_array_equals(new Uint8Array(credential.rawId), credentialID);
+ assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
+ assert_not_own_property(credential.getClientExtensionResults(), "appid");
+
+ // Check attestation
+ const attestationObject = CBOR.decode(credential.response.attestationObject);
+ if (isNoneAttestation)
+ assert_equals(attestationObject.fmt, "none");
+ else
+ assert_equals(attestationObject.fmt, "Apple");
+ // Check authData
+ const authData = decodeAuthData(attestationObject.authData);
+ assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
+ assert_equals(authData.flags, 69);
+ assert_equals(authData.counter, 0);
+ assert_equals(bytesToHexString(authData.aaguid), "00000000000000000000000000000000");
+ assert_array_equals(authData.credentialID, credentialID);
+ // Check self attestation
+ assert_true(checkPublicKey(authData.publicKey));
+ if (isNoneAttestation)
+ assert_object_equals(attestationObject.attStmt, { });
+ else {
+ assert_equals(attestationObject.attStmt.alg, -7);
+ assert_equals(attestationObject.attStmt.x5c.length, 2);
+ assert_array_equals(attestationObject.attStmt.x5c[0], Base64URL.parse(testAttestationCertificateBase64));
+ assert_array_equals(attestationObject.attStmt.x5c[1], Base64URL.parse(testAttestationIssuingCACertificateBase64));
+
+ // Check signature
+ let publicKeyData = new Uint8Array(65);
+ publicKeyData[0] = 0x04;
+ publicKeyData.set(authData.publicKey['-2'], 1);
+ publicKeyData.set(authData.publicKey['-3'], 33);
+ return crypto.subtle.importKey("raw", publicKeyData, {
name: "ECDSA",
- hash: "SHA-256"
- }, publicKey, extractRawSignature(attestationObject.attStmt.sig), attestationObject.authData).then(verified => {
- assert_true(verified);
+ namedCurve: "P-256"
+ }, false, ['verify']).then(publicKey => {
+ return crypto.subtle.verify({
+ name: "ECDSA",
+ hash: "SHA-256"
+ }, publicKey, extractRawSignature(attestationObject.attStmt.sig), attestationObject.authData).then(verified => {
+ assert_true(verified);
+ });
});
- });
- }
- }
-
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "localhost",
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "Appleseed",
- },
- challenge: Base64URL.parse("MTIzNDU2"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- }
- };
-
- return navigator.credentials.create(options).then(credential => {
- checkResult(credential);
- });
- }, "PublicKeyCredential's [[create]] with minimum options in a mock local authenticator.");
-
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "localhost",
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "Appleseed",
- },
- challenge: Base64URL.parse("MTIzNDU2"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- authenticatorSelection: { authenticatorAttachment: "platform" }
- }
- };
-
- return navigator.credentials.create(options).then(credential => {
- checkResult(credential);
- });
- }, "PublicKeyCredential's [[create]] with authenticatorSelection { 'platform' } in a mock local authenticator.");
-
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "example.com"
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "John",
- },
- challenge: asciiToUint8Array("123456"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- excludeCredentials: [
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["usb"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] }
- ]
- }
- };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
-
- return navigator.credentials.create(options).then(credential => {
- checkResult(credential);
- });
- }, "PublicKeyCredential's [[create]] with matched exclude credential ids but not transports in a mock local authenticator.");
-
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "localhost",
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "Appleseed",
- },
- challenge: Base64URL.parse("MTIzNDU2"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- attestation: "none"
}
- };
-
- return navigator.credentials.create(options).then(credential => {
- checkResult(credential);
- });
- }, "PublicKeyCredential's [[create]] with none attestation in a mock local authenticator.");
-
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "localhost",
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "Appleseed",
- },
- challenge: Base64URL.parse("MTIzNDU2"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- attestation: "indirect"
- }
- };
-
- return navigator.credentials.create(options).then(credential => {
- checkResult(credential, false);
- });
- }, "PublicKeyCredential's [[create]] with indirect attestation in a mock local authenticator.");
-
- promise_test(t => {
- const options = {
- publicKey: {
- rp: {
- name: "localhost",
- },
- user: {
- name: "John Appleseed",
- id: Base64URL.parse(testUserhandleBase64),
- displayName: "Appleseed",
- },
- challenge: Base64URL.parse("MTIzNDU2"),
- pubKeyCredParams: [{ type: "public-key", alg: -7 }],
- attestation: "direct"
- }
- };
+ }
- return navigator.credentials.create(options).then(credential => {
- checkResult(credential, false);
- });
- }, "PublicKeyCredential's [[create]] with direct attestation in a mock local authenticator.");
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "localhost",
+ },
+ user: {
+ name: userhandleBase64,
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "Appleseed",
+ },
+ challenge: Base64URL.parse("MTIzNDU2"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ }
+ };
+
+ return navigator.credentials.create(options).then(credential => {
+ checkResult(credential);
+ });
+ }, "PublicKeyCredential's [[create]] with minimum options in a mock local authenticator.");
+
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "localhost",
+ },
+ user: {
+ name: userhandleBase64,
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "Appleseed",
+ },
+ challenge: Base64URL.parse("MTIzNDU2"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ authenticatorSelection: { authenticatorAttachment: "platform" }
+ }
+ };
+
+ return navigator.credentials.create(options).then(credential => {
+ checkResult(credential);
+ });
+ }, "PublicKeyCredential's [[create]] with authenticatorSelection { 'platform' } in a mock local authenticator.");
+
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "example.com"
+ },
+ user: {
+ name: userhandleBase64,
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "John",
+ },
+ challenge: asciiToUint8Array("123456"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ excludeCredentials: [
+ { type: "public-key", id: credentialID, transports: ["usb"] },
+ { type: "public-key", id: credentialID, transports: ["nfc"] },
+ { type: "public-key", id: credentialID, transports: ["ble"] }
+ ]
+ }
+ };
+ if (window.testRunner)
+ testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, userhandleBase64);
+
+ return navigator.credentials.create(options).then(credential => {
+ checkResult(credential);
+ });
+ }, "PublicKeyCredential's [[create]] with matched exclude credential ids but not transports in a mock local authenticator.");
+
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "localhost",
+ },
+ user: {
+ name: userhandleBase64,
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "Appleseed",
+ },
+ challenge: Base64URL.parse("MTIzNDU2"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ attestation: "none"
+ }
+ };
+
+ return navigator.credentials.create(options).then(credential => {
+ checkResult(credential);
+ });
+ }, "PublicKeyCredential's [[create]] with none attestation in a mock local authenticator.");
+
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "localhost",
+ },
+ user: {
+ name: userhandleBase64,
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "Appleseed",
+ },
+ challenge: Base64URL.parse("MTIzNDU2"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ attestation: "indirect"
+ }
+ };
+
+ return navigator.credentials.create(options).then(credential => {
+ checkResult(credential, false);
+ });
+ }, "PublicKeyCredential's [[create]] with indirect attestation in a mock local authenticator.");
+
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ rp: {
+ name: "localhost",
+ },
+ user: {
+ name: userhandleBase64,
+ id: Base64URL.parse(userhandleBase64),
+ displayName: "Appleseed",
+ },
+ challenge: Base64URL.parse("MTIzNDU2"),
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+ attestation: "direct"
+ }
+ };
+
+ return navigator.credentials.create(options).then(credential => {
+ checkResult(credential, false);
+ });
+ }, "PublicKeyCredential's [[create]] with direct attestation in a mock local authenticator.");
+ })();
</script>
<script src="/resources/testharnessreport.js"></script>
<script src="./resources/util.js"></script>
<script>
- // Default mock configuration. Tests need to override if they need different configuration.
- if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false } });
+ (async function() {
+ const userhandleBase64 = generateUserhandleBase64();
+ const privateKeyBase64 = await generatePrivateKeyBase64();
+ const credentialID = await calculateCredentialID(privateKeyBase64);
+ // Default mock configuration. Tests need to override if they need different configuration.
+ if (window.testRunner)
+ testRunner.setWebAuthenticationMockConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false, preferredUserhandleBase64: userhandleBase64 } });
- promise_test(t => {
- const options = {
- publicKey: {
- challenge: asciiToUint8Array("123456"),
- allowCredentials: [
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["usb"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["internal"] }
- ],
- timeout: 10
- }
- };
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ challenge: asciiToUint8Array("123456"),
+ allowCredentials: [
+ { type: "public-key", id: credentialID, transports: ["usb"] },
+ { type: "public-key", id: credentialID, transports: ["nfc"] },
+ { type: "public-key", id: credentialID, transports: ["ble"] },
+ { type: "public-key", id: credentialID, transports: ["internal"] }
+ ],
+ timeout: 10
+ }
+ };
- return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
- }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator.");
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
+ }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator.");
- promise_test(t => {
- const options = {
- publicKey: {
- challenge: asciiToUint8Array("123456"),
- allowCredentials: [
- { type: "public-key", id: Base64URL.parse(testUserhandleBase64) }
- ],
- timeout: 10
- }
- };
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ challenge: asciiToUint8Array("123456"),
+ allowCredentials: [
+ { type: "public-key", id: Base64URL.parse(userhandleBase64) }
+ ],
+ timeout: 10
+ }
+ };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.").then(() => {
- if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
- });
- }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 2");
+ if (window.testRunner)
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.").then(() => {
+ if (window.testRunner)
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+ });
+ }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 2");
- promise_test(t => {
- const options = {
- publicKey: {
- challenge: asciiToUint8Array("123456"),
- timeout: 10
- }
- };
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ challenge: asciiToUint8Array("123456"),
+ timeout: 10
+ }
+ };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.").then(() => {
if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
- });
- }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 3");
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.").then(() => {
+ if (window.testRunner)
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+ });
+ }, "PublicKeyCredential's [[get]] with silent failure in a mock local authenticator. 3");
+ })();
</script>
<script src="/resources/testharnessreport.js"></script>
<script src="./resources/util.js"></script>
<script>
- // Default mock configuration. Tests need to override if they need different configuration.
- if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
+ (async function() {
+ const userhandleBase64 = generateUserhandleBase64();
+ const privateKeyBase64 = await generatePrivateKeyBase64();
+ const credentialID = await calculateCredentialID(privateKeyBase64);
+ // Default mock configuration. Tests need to override if they need different configuration.
+ if (window.testRunner)
+ testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false, preferredUserhandleBase64: userhandleBase64 } });
- promise_test(t => {
- const options = {
- publicKey: {
- challenge: asciiToUint8Array("123456"),
- allowCredentials: [
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["usb"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["internal"] }
- ]
- }
- };
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ challenge: asciiToUint8Array("123456"),
+ allowCredentials: [
+ { type: "public-key", id: credentialID, transports: ["usb"] },
+ { type: "public-key", id: credentialID, transports: ["nfc"] },
+ { type: "public-key", id: credentialID, transports: ["ble"] },
+ { type: "public-key", id: credentialID, transports: ["internal"] }
+ ]
+ }
+ };
- return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No matched credentials are found in the platform attached authenticator.");
- }, "PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator.");
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No matched credentials are found in the platform attached authenticator.");
+ }, "PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator.");
- promise_test(t => {
- const options = {
- publicKey: {
- challenge: asciiToUint8Array("123456"),
- allowCredentials: [
- { type: "public-key", id: Base64URL.parse(testUserhandleBase64) }
- ]
- }
- };
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ challenge: asciiToUint8Array("123456"),
+ allowCredentials: [
+ { type: "public-key", id: Base64URL.parse(userhandleBase64) }
+ ]
+ }
+ };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No matched credentials are found in the platform attached authenticator.").then(() => {
+ if (window.testRunner)
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No matched credentials are found in the platform attached authenticator.").then(() => {
if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
});
- }, "PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator. 2nd");
+ }, "PublicKeyCredential's [[get]] with no matched credentials in a mock local authenticator. 2nd");
- promise_test(t => {
- const options = {
- publicKey: {
- challenge: asciiToUint8Array("123456")
- }
- };
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ challenge: asciiToUint8Array("123456")
+ }
+ };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Couldn't get user consent.").then(() => {
if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
- });
- }, "PublicKeyCredential's [[get]] without user consent in a mock local authenticator.");
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Couldn't get user consent.").then(() => {
+ if (window.testRunner)
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
+ });
+ }, "PublicKeyCredential's [[get]] without user consent in a mock local authenticator.");
- promise_test(t => {
- const options = {
- publicKey: {
- challenge: asciiToUint8Array("123456"),
- allowCredentials: [
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["usb"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["nfc"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["ble"] }
- ],
- timeout: 10
- }
- };
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ challenge: asciiToUint8Array("123456"),
+ allowCredentials: [
+ { type: "public-key", id: credentialID, transports: ["usb"] },
+ { type: "public-key", id: credentialID, transports: ["nfc"] },
+ { type: "public-key", id: credentialID, transports: ["ble"] }
+ ],
+ timeout: 10
+ }
+ };
- return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
- }, "PublicKeyCredential's [[get]] with timeout in a mock local authenticator.");
+ return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Operation timed out.");
+ }, "PublicKeyCredential's [[get]] with timeout in a mock local authenticator.");
+ })();
</script>
<script src="/resources/testharnessreport.js"></script>
<script src="./resources/util.js"></script>
<script>
- // Default mock configuration. Tests need to override if they need different configuration.
- if (window.testRunner)
- testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
-
- function checkResult(credential)
- {
+ (async function() {
+ const userhandleBase64 = generateUserhandleBase64();
+ const privateKeyBase64 = await generatePrivateKeyBase64();
+ const credentialID = await calculateCredentialID(privateKeyBase64);
+ // Default mock configuration. Tests need to override if they need different configuration.
if (window.testRunner)
- testRunner.cleanUpKeychain(testRpId);
+ testRunner.setWebAuthenticationMockConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false, preferredUserhandleBase64: userhandleBase64 } });
+
+ function checkResult(credential)
+ {
+ if (window.testRunner)
+ testRunner.cleanUpKeychain(testRpId, userhandleBase64);
- // Check respond
- assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testCredentialIdBase64));
- assert_equals(credential.type, 'public-key');
- assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testCredentialIdBase64));
- assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
- assert_equals(bytesToHexString(credential.response.userHandle), "00010203040506070809");
- assert_not_own_property(credential.getClientExtensionResults(), "appid");
+ // Check respond
+ assert_array_equals(Base64URL.parse(credential.id), credentialID);
+ assert_equals(credential.type, 'public-key');
+ assert_array_equals(new Uint8Array(credential.rawId), credentialID);
+ assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
+ assert_array_equals(new Uint8Array(credential.response.userHandle), Base64URL.parse(userhandleBase64));
+ assert_not_own_property(credential.getClientExtensionResults(), "appid");
- // Check authData
- const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData));
- assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
- assert_equals(authData.flags, 5);
- assert_equals(authData.counter, 0);
+ // Check authData
+ const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData));
+ assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
+ assert_equals(authData.flags, 5);
+ assert_equals(authData.counter, 0);
- // Check signature
- return crypto.subtle.importKey("raw", Base64URL.parse(testES256PublicKeyBase64), { name: "ECDSA", namedCurve: "P-256" }, false, ['verify']).then( publicKey => {
- return crypto.subtle.digest("sha-256", credential.response.clientDataJSON).then ( hash => {
- // credential.response.signature is in ASN.1 and WebCrypto expect signatures provides in r|s.
- return crypto.subtle.verify({name: "ECDSA", hash: "SHA-256"}, publicKey, extractRawSignature(credential.response.signature), concatenateBuffers(credential.response.authenticatorData, hash)).then( verified => {
- assert_true(verified);
- assert_not_own_property(credential.getClientExtensionResults(), "appid");
+ // Check signature
+ return crypto.subtle.importKey("raw", Base64URL.parse(privateKeyBase64).slice(0, 65), { name: "ECDSA", namedCurve: "P-256" }, false, ['verify']).then( publicKey => {
+ return crypto.subtle.digest("sha-256", credential.response.clientDataJSON).then ( hash => {
+ // credential.response.signature is in ASN.1 and WebCrypto expect signatures provides in r|s.
+ return crypto.subtle.verify({name: "ECDSA", hash: "SHA-256"}, publicKey, extractRawSignature(credential.response.signature), concatenateBuffers(credential.response.authenticatorData, hash)).then( verified => {
+ assert_true(verified);
+ assert_not_own_property(credential.getClientExtensionResults(), "appid");
+ });
});
});
- });
- }
+ }
- promise_test(t => {
- const options = {
- publicKey: {
- challenge: Base64URL.parse("MTIzNDU2")
- }
- };
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ challenge: Base64URL.parse("MTIzNDU2")
+ }
+ };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return navigator.credentials.get(options).then(credential => {
- return checkResult(credential);
- });
- }, "PublicKeyCredential's [[get]] with minimum options in a mock local authenticator.");
+ if (window.testRunner)
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return navigator.credentials.get(options).then(credential => {
+ return checkResult(credential);
+ });
+ }, "PublicKeyCredential's [[get]] with minimum options in a mock local authenticator.");
- promise_test(t => {
- const options = {
- publicKey: {
- challenge: Base64URL.parse("MTIzNDU2"),
- allowCredentials: [
- { type: "public-key", id: Base64URL.parse(testUserhandleBase64), transports: ["internal"] },
- { type: "public-key", id: Base64URL.parse(testCredentialIdBase64), transports: ["internal"] }
- ]
- }
- };
+ promise_test(t => {
+ const options = {
+ publicKey: {
+ challenge: Base64URL.parse("MTIzNDU2"),
+ allowCredentials: [
+ { type: "public-key", id: Base64URL.parse(userhandleBase64), transports: ["internal"] },
+ { type: "public-key", id: credentialID, transports: ["internal"] }
+ ]
+ }
+ };
- if (window.testRunner)
- testRunner.addTestKeyToKeychain(testES256PrivateKeyBase64, testRpId, testUserhandleBase64);
- return navigator.credentials.get(options).then(credential => {
- return checkResult(credential);
- });
- }, "PublicKeyCredential's [[get]] with matched allow credentials in a mock local authenticator.");
+ if (window.testRunner)
+ testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
+ return navigator.credentials.get(options).then(credential => {
+ return checkResult(credential);
+ });
+ }, "PublicKeyCredential's [[get]] with matched allow credentials in a mock local authenticator.");
+ })();
</script>
"BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
"PH76c0+WFOzZKslPyyFse4goGIW2R7k9VHLPEZl5nfnBgEVFh5zev+/xpHQIvuq6" +
"RQ==";
-const testES256PublicKeyBase64 =
- "BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
- "PH76c0+WFOzZKslPyyFse4g=";
const testRpId = "localhost";
const testUserhandleBase64 = "AAECAwQFBgcICQ==";
const testAttestationCertificateBase64 =
assert_equals(authData.flags, 1);
assert_equals(authData.counter, 59);
}
+
+function generateUserhandleBase64()
+{
+ let buffer = new Uint8Array(16);
+ crypto.getRandomValues(buffer);
+ return btoa(String.fromCharCode.apply(0, buffer));
+}
+
+async function generatePrivateKeyBase64()
+{
+ const algorithmKeyGen = {
+ name: "ECDSA",
+ namedCurve: "P-256"
+ };
+ const extractable = true;
+
+ const keyPair = await crypto.subtle.generateKey(algorithmKeyGen, extractable, ["sign", "verify"]);
+ const jwkPrivateKey = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
+
+ const x = Base64URL.parse(jwkPrivateKey.x);
+ const y = Base64URL.parse(jwkPrivateKey.y);
+ const d = Base64URL.parse(jwkPrivateKey.d);
+
+ let buffer = new Uint8Array(x.length + y.length + d.length + 1);
+ buffer[0] = 0x04;
+ let pos = 1;
+ buffer.set(x, pos);
+ pos += x.length;
+ buffer.set(y, pos);
+ pos += y.length;
+ buffer.set(d, pos);
+
+ return btoa(String.fromCharCode.apply(0, buffer));
+}
+
+async function calculateCredentialID(privateKeyBase64) {
+ const privateKey = Base64URL.parse(privateKeyBase64);
+ const publicKey = privateKey.slice(0, 65);
+ return new Uint8Array(await crypto.subtle.digest("sha-1", publicKey));
+}
http/wpt/webauthn/public-key-credential-get-failure-local.https.html [ Skip ]
http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html [ Skip ]
http/wpt/webauthn/public-key-credential-get-success-local.https.html [ Skip ]
-http/wpt/credential-management/credentialscontainer-store-basics.https.html [ Skip ]
-http/wpt/webauthn/idl.https.html [ Skip ]
# NFC support require Catalina+, therefore skip them for HighSierra and Mojave.
[ HighSierra Mojave ] http/wpt/webauthn/ctap-nfc-failure.https.html [ Skip ]
+2019-09-16 Jiewen Tan <jiewen_tan@apple.com>
+
+ [WebAuthn] LocalAuthenticator tests are failing on internal bots
+ https://bugs.webkit.org/show_bug.cgi?id=201844
+ <rdar://problem/54278693>
+
+ Reviewed by Brent Fulgham.
+
+ This patch adds a way for mock tests to select a credential in getAssertion
+ ceremonies such that a test can ensure it always uses the credential it manages.
+ Credentials managed by other test could be deleted at anytime.
+
+ * UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
+ (WKWebsiteDataStoreSetWebAuthenticationMockConfiguration):
+ * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
+ (WebKit::LocalAuthenticator::getAssertion):
+ * UIProcess/WebAuthentication/Cocoa/LocalConnection.h:
+ * UIProcess/WebAuthentication/Cocoa/LocalConnection.mm:
+ (WebKit::LocalConnection::selectCredential const):
+ * UIProcess/WebAuthentication/Mock/MockLocalConnection.h:
+ * UIProcess/WebAuthentication/Mock/MockLocalConnection.mm:
+ (WebKit::MockLocalConnection::selectCredential const):
+ * UIProcess/WebAuthentication/Mock/MockWebAuthenticationConfiguration.h:
+
2019-09-23 David Quesada <david_quesada@apple.com>
[iOS] REGRESSION(r250151): Occasional assertion failures in ShareableBitmap::~ShareableBitmap()
local.userCertificateBase64 = WebKit::toImpl(static_cast<WKStringRef>(WKDictionaryGetItemForKey(localRef, adoptWK(WKStringCreateWithUTF8CString("UserCertificateBase64")).get())))->string();
local.intermediateCACertificateBase64 = WebKit::toImpl(static_cast<WKStringRef>(WKDictionaryGetItemForKey(localRef, adoptWK(WKStringCreateWithUTF8CString("IntermediateCACertificateBase64")).get())))->string();
}
+ if (auto preferredUserhandleBase64Ref = static_cast<WKStringRef>(WKDictionaryGetItemForKey(localRef, adoptWK(WKStringCreateWithUTF8CString("PreferredUserhandleBase64")).get())))
+ local.preferredUserhandleBase64 = WebKit::toImpl(preferredUserhandleBase64Ref)->string();
configuration.local = WTFMove(local);
}
}
// Step 6.
- // FIXME(rdar://problem/35900534): We don't have an UI to prompt users for selecting intersectedCredentials, and therefore we always use the first one for now.
- NSDictionary *selectedCredentialAttributes = intersectedCredentialsAttributes[0];
+ auto *selectedCredentialAttributes = m_connection->selectCredential(intersectedCredentialsAttributes);
// Step 7. Get user consent.
// FIXME(rdar://problem/35900593): Update to a formal UI.
virtual void getUserConsent(const String& reason, UserConsentCallback&&) const;
virtual void getUserConsent(const String& reason, SecAccessControlRef, UserConsentContextCallback&&) const;
virtual void getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&&) const;
+ virtual NSDictionary *selectCredential(const NSArray *) const;
};
} // namespace WebKit
#endif // HAVE(DEVICE_IDENTITY)
}
+NSDictionary *LocalConnection::selectCredential(const NSArray *credentials) const
+{
+ // FIXME(rdar://problem/35900534): We don't have an UI to prompt users for selecting intersectedCredentials, and therefore we always use the first one for now.
+ return credentials[0];
+}
+
} // namespace WebKit
#endif // ENABLE(WEB_AUTHN)
void getUserConsent(const String& reason, UserConsentCallback&&) const final;
void getUserConsent(const String& reason, SecAccessControlRef, UserConsentContextCallback&&) const final;
void getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&&) const final;
+ NSDictionary *selectCredential(const NSArray *) const final;
MockWebAuthenticationConfiguration m_configuration;
};
});
}
+NSDictionary *MockLocalConnection::selectCredential(const NSArray *credentials) const
+{
+ auto preferredUserhandle = adoptNS([[NSData alloc] initWithBase64EncodedString:m_configuration.local->preferredUserhandleBase64 options:0]);
+ for (NSDictionary *credential : credentials) {
+ if ([credential[(id)kSecAttrApplicationTag] isEqualToData:preferredUserhandle.get()])
+ return credential;
+ }
+ ASSERT_NOT_REACHED();
+ return nil;
+}
+
} // namespace WebKit
#endif // ENABLE(WEB_AUTHN)
String privateKeyBase64;
String userCertificateBase64;
String intermediateCACertificateBase64;
+ String preferredUserhandleBase64;
};
struct Hid {
+2019-09-16 Jiewen Tan <jiewen_tan@apple.com>
+
+ [WebAuthn] LocalAuthenticator tests are failing on internal bots
+ https://bugs.webkit.org/show_bug.cgi?id=201844
+ <rdar://problem/54278693>
+
+ Reviewed by Brent Fulgham.
+
+ LocalAuthenticator tests utilize Keychain for mock testing. Unlike iOS simulator tests which
+ each test runner is running in different simulator containers, all test runners are running
+ in the same macOS container in macOS. Therefore, Keychain is shared among all test runners
+ in macOS while it is not in iOS simulators. And therefore, race conditions would happen in
+ macOS which make the current tests flaky given they don't consider race conditions.
+
+ This patch then makes each test generate a random credential, and thus no other tests would
+ be able to access it, and therefore eliminate any race condition. To support this, a few new
+ functionalities are introduced to the mock test infrastructure as well:
+ 1) TestRunner.cleanUpKeychain accepts a new parameter to more precisely identify an item.
+ 2) WebAuthenticationMockConfiguration.Local has a new member to uniquely select a credential
+ for getAssertion ceremony when multiple presents.
+
+ * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+ * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+ (WTR::TestRunner::setWebAuthenticationMockConfiguration):
+ (WTR::TestRunner::cleanUpKeychain):
+ * WebKitTestRunner/InjectedBundle/TestRunner.h:
+ * WebKitTestRunner/TestController.h:
+ * WebKitTestRunner/TestInvocation.cpp:
+ (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
+ * WebKitTestRunner/TestController.cpp:
+ (WTR::TestController::cleanUpKeychain):
+ * WebKitTestRunner/cocoa/TestControllerCocoa.mm:
+ (WTR::TestController::cleanUpKeychain):
+
2019-09-20 Aakash Jain <aakash_jain@apple.com>
[EWS] JSC queues should re-build ToT and compare results on build failure
// WebAuthN
void setWebAuthenticationMockConfiguration(object configuration);
void addTestKeyToKeychain(DOMString privateKeyBase64, DOMString attrLabel, DOMString applicationTagBase64);
- void cleanUpKeychain(DOMString attrLabel);
+ void cleanUpKeychain(DOMString attrLabel, optional DOMString applicationTagBase64);
boolean keyExistsInKeychain(DOMString attrLabel, DOMString applicationTagBase64);
// Ad Click Attribution
JSRetainPtr<JSStringRef> intermediateCACertificateBase64PropertyName(Adopt, JSStringCreateWithUTF8CString("intermediateCACertificateBase64"));
JSValueRef intermediateCACertificateBase64Value = JSObjectGetProperty(context, local, intermediateCACertificateBase64PropertyName.get(), 0);
if (!JSValueIsString(context, intermediateCACertificateBase64Value))
- return;
+ return;
localKeys.append(adoptWK(WKStringCreateWithUTF8CString("PrivateKeyBase64")));
localValues.append(toWK(adopt(JSValueToStringCopy(context, privateKeyBase64Value, 0)).get()));
localValues.append(toWK(adopt(JSValueToStringCopy(context, intermediateCACertificateBase64Value, 0)).get()));
}
+ JSRetainPtr<JSStringRef> preferredUserhandleBase64PropertyName(Adopt, JSStringCreateWithUTF8CString("preferredUserhandleBase64"));
+ JSValueRef preferredUserhandleBase64Value = JSObjectGetProperty(context, local, preferredUserhandleBase64PropertyName.get(), 0);
+ if (!JSValueIsUndefined(context, preferredUserhandleBase64Value) && !JSValueIsNull(context, preferredUserhandleBase64Value)) {
+ if (!JSValueIsString(context, preferredUserhandleBase64Value))
+ return;
+
+ localKeys.append(adoptWK(WKStringCreateWithUTF8CString("PreferredUserhandleBase64")));
+ localValues.append(toWK(adopt(JSValueToStringCopy(context, preferredUserhandleBase64Value, 0)).get()));
+ }
+
Vector<WKStringRef> rawLocalKeys;
Vector<WKTypeRef> rawLocalValues;
rawLocalKeys.resize(localKeys.size());
WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
}
-void TestRunner::cleanUpKeychain(JSStringRef attrLabel)
+void TestRunner::cleanUpKeychain(JSStringRef attrLabel, JSStringRef applicationTagBase64)
{
+ Vector<WKRetainPtr<WKStringRef>> keys;
+ Vector<WKRetainPtr<WKTypeRef>> values;
+
+ keys.append(adoptWK(WKStringCreateWithUTF8CString("AttrLabel")));
+ values.append(toWK(attrLabel));
+
+ if (applicationTagBase64) {
+ keys.append(adoptWK(WKStringCreateWithUTF8CString("ApplicationTag")));
+ values.append(toWK(applicationTagBase64));
+ }
+
+ Vector<WKStringRef> rawKeys;
+ Vector<WKTypeRef> rawValues;
+ rawKeys.resize(keys.size());
+ rawValues.resize(values.size());
+
+ for (size_t i = 0; i < keys.size(); ++i) {
+ rawKeys[i] = keys[i].get();
+ rawValues[i] = values[i].get();
+ }
+
WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CleanUpKeychain"));
- WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithJSString(attrLabel));
+ WKRetainPtr<WKDictionaryRef> messageBody = adoptWK(WKDictionaryCreate(rawKeys.data(), rawValues.data(), rawKeys.size()));
WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
}
void setWebAuthenticationMockConfiguration(JSValueRef);
// FIXME(189876)
void addTestKeyToKeychain(JSStringRef privateKeyBase64, JSStringRef attrLabel, JSStringRef applicationTagBase64);
- void cleanUpKeychain(JSStringRef attrLabel);
+ void cleanUpKeychain(JSStringRef attrLabel, JSStringRef applicationTagBase64);
bool keyExistsInKeychain(JSStringRef attrLabel, JSStringRef applicationTagBase64);
void setCanHandleHTTPSServerTrustEvaluation(bool canHandle);
{
}
-void TestController::cleanUpKeychain(const String&)
+void TestController::cleanUpKeychain(const String&, const String&)
{
}
void setWebAuthenticationMockConfiguration(WKDictionaryRef);
void addTestKeyToKeychain(const String& privateKeyBase64, const String& attrLabel, const String& applicationTagBase64);
- void cleanUpKeychain(const String& attrLabel);
+ void cleanUpKeychain(const String& attrLabel, const String& applicationTagBase64);
bool keyExistsInKeychain(const String& attrLabel, const String& applicationTagBase64);
#if PLATFORM(COCOA)
}
if (WKStringIsEqualToUTF8CString(messageName, "CleanUpKeychain")) {
- ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
- TestController::singleton().cleanUpKeychain(toWTFString(static_cast<WKStringRef>(messageBody)));
+ ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+ WKDictionaryRef testDictionary = static_cast<WKDictionaryRef>(messageBody);
+
+ WKRetainPtr<WKStringRef> attrLabelKey = adoptWK(WKStringCreateWithUTF8CString("AttrLabel"));
+ WKStringRef attrLabelWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(testDictionary, attrLabelKey.get()));
+
+ WKRetainPtr<WKStringRef> applicationTagKey = adoptWK(WKStringCreateWithUTF8CString("ApplicationTag"));
+ WKStringRef applicationTagWK = static_cast<WKStringRef>(WKDictionaryGetItemForKey(testDictionary, applicationTagKey.get()));
+
+ TestController::singleton().cleanUpKeychain(toWTFString(attrLabelWK), applicationTagWK ? toWTFString(applicationTagWK) : String());
return nullptr;
}
ASSERT_UNUSED(status, !status);
}
-void TestController::cleanUpKeychain(const String& attrLabel)
+void TestController::cleanUpKeychain(const String& attrLabel, const String& applicationTagBase64)
{
- NSDictionary* deleteQuery = @{
- (id)kSecClass: (id)kSecClassKey,
- (id)kSecAttrLabel: attrLabel,
+ auto deleteQuery = adoptNS([[NSMutableDictionary alloc] init]);
+ [deleteQuery setObject:(id)kSecClassKey forKey:(id)kSecClass];
+ [deleteQuery setObject:attrLabel forKey:(id)kSecAttrLabel];
#if HAVE(DATA_PROTECTION_KEYCHAIN)
- (id)kSecUseDataProtectionKeychain: @YES
+ [deleteQuery setObject:@YES forKey:(id)kSecUseDataProtectionKeychain];
#else
- (id)kSecAttrNoLegacy: @YES
+ [deleteQuery setObject:@YES forKey:(id)kSecAttrNoLegacy];
#endif
- };
- SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
+ if (!!applicationTagBase64)
+ [deleteQuery setObject:adoptNS([[NSData alloc] initWithBase64EncodedString:applicationTagBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get() forKey:(id)kSecAttrApplicationTag];
+
+ SecItemDelete((__bridge CFDictionaryRef)deleteQuery.get());
}
bool TestController::keyExistsInKeychain(const String& attrLabel, const String& applicationTagBase64)