| <!DOCTYPE html> |
| <title>Web Authentication API: PublicKeyCredential's [[create]] success cases with a mock local authenticator.</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="./resources/util.js"></script> |
| <script src="./resources/cbor.js"></script> |
| <script> |
| function checkResult(credential, credentialID, isNoneAttestation = true) |
| { |
| // Check keychain |
| if (window.testRunner) { |
| assert_true(testRunner.keyExistsInKeychain(testRpId, base64encode(credentialID))); |
| testRunner.cleanUpKeychain(testRpId, base64encode(credentialID)); |
| } |
| |
| // 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)); |
| } |
| } |
| |
| promise_test(async t => { |
| const privateKeyBase64 = await generatePrivateKeyBase64(); |
| const credentialID = await calculateCredentialID(privateKeyBase64); |
| const userhandleBase64 = generateUserhandleBase64(); |
| if (window.internals) |
| internals.setMockWebAuthenticationConfiguration({ |
| local: { |
| userVerification: "yes", |
| acceptAttestation: false, |
| privateKeyBase64: privateKeyBase64, |
| } |
| }); |
| |
| 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, credentialID); |
| }); |
| }, "PublicKeyCredential's [[create]] with minimum options in a mock local authenticator."); |
| |
| promise_test(async t => { |
| const privateKeyBase64 = await generatePrivateKeyBase64(); |
| const credentialID = await calculateCredentialID(privateKeyBase64); |
| const userhandleBase64 = generateUserhandleBase64(); |
| if (window.internals) |
| internals.setMockWebAuthenticationConfiguration({ |
| local: { |
| userVerification: "yes", |
| acceptAttestation: false, |
| privateKeyBase64: privateKeyBase64, |
| } |
| }); |
| |
| 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, credentialID); |
| }); |
| }, "PublicKeyCredential's [[create]] with authenticatorSelection { 'platform' } in a mock local authenticator."); |
| |
| promise_test(async t => { |
| const privateKeyBase64 = await generatePrivateKeyBase64(); |
| const credentialID = await calculateCredentialID(privateKeyBase64); |
| const userhandleBase64 = generateUserhandleBase64(); |
| if (window.internals) |
| internals.setMockWebAuthenticationConfiguration({ |
| local: { |
| userVerification: "yes", |
| acceptAttestation: false, |
| privateKeyBase64: privateKeyBase64, |
| } |
| }); |
| |
| const anotherPrivateKeyBase64 = await generatePrivateKeyBase64(); |
| const anotherCredentialID = await calculateCredentialID(anotherPrivateKeyBase64); |
| 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: anotherCredentialID, transports: ["usb"] }, |
| { type: "public-key", id: anotherCredentialID, transports: ["nfc"] }, |
| { type: "public-key", id: anotherCredentialID, transports: ["ble"] } |
| ] |
| } |
| }; |
| if (window.testRunner) |
| testRunner.addTestKeyToKeychain(anotherPrivateKeyBase64, testRpId, testUserEntityBundleBase64); |
| |
| return navigator.credentials.create(options).then(credential => { |
| checkResult(credential, credentialID); |
| if (window.testRunner) |
| testRunner.cleanUpKeychain(testRpId, base64encode(anotherCredentialID)); |
| }); |
| }, "PublicKeyCredential's [[create]] with matched exclude credential ids but not transports in a mock local authenticator."); |
| |
| promise_test(async t => { |
| const privateKeyBase64 = await generatePrivateKeyBase64(); |
| const credentialID = await calculateCredentialID(privateKeyBase64); |
| const userhandleBase64 = generateUserhandleBase64(); |
| if (window.internals) |
| internals.setMockWebAuthenticationConfiguration({ |
| local: { |
| userVerification: "yes", |
| acceptAttestation: false, |
| privateKeyBase64: privateKeyBase64, |
| } |
| }); |
| |
| 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, credentialID); |
| }); |
| }, "PublicKeyCredential's [[create]] with none attestation in a mock local authenticator."); |
| |
| promise_test(async t => { |
| const privateKeyBase64 = await generatePrivateKeyBase64(); |
| const credentialID = await calculateCredentialID(privateKeyBase64); |
| const userhandleBase64 = generateUserhandleBase64(); |
| if (window.internals) |
| internals.setMockWebAuthenticationConfiguration({ |
| local: { |
| userVerification: "yes", |
| acceptAttestation: true, |
| privateKeyBase64: privateKeyBase64, |
| userCertificateBase64: testAttestationCertificateBase64, |
| intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64 |
| } |
| }); |
| |
| 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, credentialID, false); |
| }); |
| }, "PublicKeyCredential's [[create]] with indirect attestation in a mock local authenticator."); |
| |
| promise_test(async t => { |
| const privateKeyBase64 = await generatePrivateKeyBase64(); |
| const credentialID = await calculateCredentialID(privateKeyBase64); |
| const userhandleBase64 = generateUserhandleBase64(); |
| if (window.internals) |
| internals.setMockWebAuthenticationConfiguration({ |
| local: { |
| userVerification: "yes", |
| acceptAttestation: true, |
| privateKeyBase64: privateKeyBase64, |
| userCertificateBase64: testAttestationCertificateBase64, |
| intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64 |
| } |
| }); |
| |
| 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, credentialID, false); |
| }); |
| }, "PublicKeyCredential's [[create]] with direct attestation in a mock local authenticator."); |
| |
| promise_test(async t => { |
| const privateKeyBase64 = await generatePrivateKeyBase64(); |
| const credentialID = await calculateCredentialID(privateKeyBase64); |
| const userhandleBase64 = generateUserhandleBase64(); |
| if (window.internals) |
| internals.setMockWebAuthenticationConfiguration({ |
| local: { |
| userVerification: "yes", |
| acceptAttestation: false, |
| privateKeyBase64: privateKeyBase64, |
| } |
| }); |
| |
| 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 }], |
| } |
| }; |
| |
| const anotherPrivateKeyBase64 = await generatePrivateKeyBase64(); |
| const anotherCredentialID = await calculateCredentialID(anotherPrivateKeyBase64); |
| if (window.internals) |
| testRunner.addTestKeyToKeychain(anotherPrivateKeyBase64, testRpId, base64encode(CBOR.encode({ "id": Base64URL.parse(userhandleBase64), "name": userhandleBase64 }))); |
| |
| return navigator.credentials.create(options).then(credential => { |
| checkResult(credential, credentialID); |
| assert_false(testRunner.keyExistsInKeychain(testRpId, base64encode(anotherCredentialID))); |
| }); |
| }, "PublicKeyCredential's [[create]] with duplicate credential in a mock local authenticator."); |
| |
| promise_test(async t => { |
| const privateKeyBase64 = await generatePrivateKeyBase64(); |
| const credentialID = await calculateCredentialID(privateKeyBase64); |
| const userhandleBase64 = generateUserhandleBase64(); |
| if (window.internals) |
| internals.setMockWebAuthenticationConfiguration({ |
| local: { |
| userVerification: "yes", |
| acceptAttestation: true, |
| privateKeyBase64: privateKeyBase64, |
| userCertificateBase64: testAttestationCertificateBase64, |
| intermediateCACertificateBase64: testAttestationIssuingCACertificateBase64 |
| } |
| }); |
| |
| 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" |
| } |
| }; |
| |
| const anotherPrivateKeyBase64 = await generatePrivateKeyBase64(); |
| const anotherCredentialID = await calculateCredentialID(anotherPrivateKeyBase64); |
| if (window.internals) |
| testRunner.addTestKeyToKeychain(anotherPrivateKeyBase64, testRpId, base64encode(CBOR.encode({ "id": Base64URL.parse(userhandleBase64), "name": userhandleBase64 }))); |
| |
| return navigator.credentials.create(options).then(credential => { |
| checkResult(credential, credentialID, false); |
| assert_false(testRunner.keyExistsInKeychain(testRpId, base64encode(anotherCredentialID))); |
| }); |
| }, "PublicKeyCredential's [[create]] with duplicate credential in a mock local authenticator. 2"); |
| </script> |