<!DOCTYPE html><!-- webkit-test-runner [ WebAuthenticationModernEnabled=false ] -->
<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, isUV = 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");
        if (isUV)
            assert_equals(authData.flags, 69);
        else
            assert_equals(authData.flags, 65);
        assert_equals(authData.counter, 0);
        if (isNoneAttestation)
            assert_equals(bytesToHexString(authData.aaguid), "00000000000000000000000000000000");
        else
            assert_equals(bytesToHexString(authData.aaguid), "f24a8e70d0d3f82c293732523cc4de5a");
        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.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 = "BKeelnM8D7ykBNa79wacy6FW8UYZ+PG0Cr2Dr4Rzo+qJHExNkPVdzQ1/hjrqg8iY3IzM/NUvJJOzmtuchS6TLqTfzmsXsVdApUn9kx4Lh/cv01hbXt7/Q8kAbubi8Mqmqw==";
        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 => {
            assert_equals(bytesToHexString(credential.response.getPublicKey()), "3059301306072a8648ce3d020106082a8648ce3d03010703420004a79e96733c0fbca404d6bbf7069ccba156f14619f8f1b40abd83af8473a3ea891c4c4d90f55dcd0d7f863aea83c898dc8cccfcd52f2493b39adb9c852e932ea4");
            assert_equals(credential.response.getPublicKeyAlgorithm(), -7);
        });
    }, "PublicKeyCredential's [[create]] with minimum options in a mock local authenticator checking getPublicKey()");

    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");

    promise_test(async t => {
        const privateKeyBase64 = await generatePrivateKeyBase64();
        const credentialID = await calculateCredentialID(privateKeyBase64);
        const userhandleBase64 = generateUserhandleBase64();
        if (window.internals)
            internals.setMockWebAuthenticationConfiguration({
                local: {
                    userVerification: "presence",
                    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, true, false);
        });
    }, "PublicKeyCredential's [[create]] with user presence in a mock local authenticator.");
</script>
