| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Copyright (C) 2019-2021 Apple Inc. All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "config.h" |
| |
| #if ENABLE(WEB_AUTHN) |
| |
| #include "FidoTestData.h" |
| #include <WebCore/FidoConstants.h> |
| #include <WebCore/PublicKeyCredentialCreationOptions.h> |
| #include <WebCore/PublicKeyCredentialRequestOptions.h> |
| #include <WebCore/U2fCommandConstructor.h> |
| #include <WebCore/WebAuthenticationConstants.h> |
| #include <WebCore/WebAuthenticationUtils.h> |
| |
| namespace TestWebKitAPI { |
| using namespace WebCore; |
| using namespace fido; |
| |
| PublicKeyCredentialCreationOptions constructMakeCredentialRequest() |
| { |
| PublicKeyCredentialCreationOptions::RpEntity rp; |
| rp.id = "acme.com"_s; |
| rp.name = "acme.com"_s; |
| |
| PublicKeyCredentialCreationOptions::UserEntity user; |
| user.id = WebCore::toBufferSource(TestData::kUserId, sizeof(TestData::kUserId)); |
| user.name = "johnpsmith@example.com"_s; |
| user.displayName = "John P. Smith"_s; |
| user.icon = "https://pics.acme.com/00/p/aBjjjpqPb.png"_s; |
| |
| PublicKeyCredentialCreationOptions::Parameters params; |
| params.type = PublicKeyCredentialType::PublicKey; |
| params.alg = COSE::ES256; |
| |
| AuthenticationExtensionsClientInputs extensions; |
| extensions.googleLegacyAppidSupport = false; |
| |
| PublicKeyCredentialCreationOptions options; |
| options.rp = WTFMove(rp); |
| options.user = WTFMove(user); |
| options.pubKeyCredParams.append(WTFMove(params)); |
| options.extensions = WTFMove(extensions); |
| |
| return options; |
| } |
| |
| PublicKeyCredentialCreationOptions constructMakeCredentialRequestWithGoogleLegacyAppidSupport() |
| { |
| auto options = constructMakeCredentialRequest(); |
| options.extensions->googleLegacyAppidSupport = true; |
| return options; |
| } |
| |
| PublicKeyCredentialRequestOptions constructGetAssertionRequest() |
| { |
| PublicKeyCredentialRequestOptions options; |
| options.rpId = "acme.com"_s; |
| return options; |
| } |
| |
| TEST(U2fCommandConstructorTest, TestConvertCtapMakeCredentialToU2fRegister) |
| { |
| const auto makeCredentialParam = constructMakeCredentialRequest(); |
| |
| EXPECT_TRUE(isConvertibleToU2fRegisterCommand(makeCredentialParam)); |
| |
| const auto u2fRegisterCommand = convertToU2fRegisterCommand(convertBytesToVector(TestData::kClientDataHash, sizeof(TestData::kClientDataHash)), makeCredentialParam); |
| ASSERT_TRUE(u2fRegisterCommand); |
| EXPECT_EQ(*u2fRegisterCommand, convertBytesToVector(TestData::kU2fRegisterCommandApdu, sizeof(TestData::kU2fRegisterCommandApdu))); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestConvertCtapMakeCredentialToU2fRegisterWithGoogleLegacyAppidSupport) |
| { |
| const auto makeCredentialParam = constructMakeCredentialRequestWithGoogleLegacyAppidSupport(); |
| |
| EXPECT_TRUE(isConvertibleToU2fRegisterCommand(makeCredentialParam)); |
| |
| const auto u2fRegisterCommand = convertToU2fRegisterCommand(convertBytesToVector(TestData::kClientDataHash, sizeof(TestData::kClientDataHash)), makeCredentialParam); |
| ASSERT_TRUE(u2fRegisterCommand); |
| EXPECT_EQ(*u2fRegisterCommand, convertBytesToVector(TestData::kU2fRegisterCommandApduWithGoogleLegacyAppidSupport, sizeof(TestData::kU2fRegisterCommandApduWithGoogleLegacyAppidSupport))); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestConvertCtapMakeCredentialToU2fCheckOnlySign) |
| { |
| auto makeCredentialParam = constructMakeCredentialRequest(); |
| PublicKeyCredentialDescriptor credentialDescriptor; |
| credentialDescriptor.type = PublicKeyCredentialType::PublicKey; |
| credentialDescriptor.id = WebCore::toBufferSource(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)); |
| Vector<PublicKeyCredentialDescriptor> excludeList; |
| excludeList.append(credentialDescriptor); |
| makeCredentialParam.excludeCredentials = WTFMove(excludeList); |
| EXPECT_TRUE(isConvertibleToU2fRegisterCommand(makeCredentialParam)); |
| |
| const auto u2fCheckOnlySign = convertToU2fCheckOnlySignCommand(convertBytesToVector(TestData::kClientDataHash, sizeof(TestData::kClientDataHash)), makeCredentialParam, credentialDescriptor); |
| ASSERT_TRUE(u2fCheckOnlySign); |
| EXPECT_EQ(*u2fCheckOnlySign, convertBytesToVector(TestData::kU2fCheckOnlySignCommandApdu, sizeof(TestData::kU2fCheckOnlySignCommandApdu))); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestConvertCtapMakeCredentialToU2fCheckOnlySignWithInvalidCredentialType) |
| { |
| auto makeCredentialParam = constructMakeCredentialRequest(); |
| PublicKeyCredentialDescriptor credentialDescriptor; |
| credentialDescriptor.type = static_cast<PublicKeyCredentialType>(-1); |
| credentialDescriptor.id = WebCore::toBufferSource(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)); |
| Vector<PublicKeyCredentialDescriptor> excludeList; |
| excludeList.append(credentialDescriptor); |
| makeCredentialParam.excludeCredentials = WTFMove(excludeList); |
| EXPECT_TRUE(isConvertibleToU2fRegisterCommand(makeCredentialParam)); |
| |
| const auto u2fCheckOnlySign = convertToU2fCheckOnlySignCommand(convertBytesToVector(TestData::kClientDataHash, sizeof(TestData::kClientDataHash)), makeCredentialParam, credentialDescriptor); |
| EXPECT_FALSE(u2fCheckOnlySign); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestU2fRegisterCredentialAlgorithmRequirement) |
| { |
| PublicKeyCredentialCreationOptions::RpEntity rp; |
| rp.id = "acme.com"_s; |
| rp.name = "acme.com"_s; |
| |
| PublicKeyCredentialCreationOptions::UserEntity user; |
| user.id = WebCore::toBufferSource(TestData::kUserId, sizeof(TestData::kUserId)); |
| user.name = "johnpsmith@example.com"_s; |
| user.displayName = "John P. Smith"_s; |
| user.icon = "https://pics.acme.com/00/p/aBjjjpqPb.png"_s; |
| |
| PublicKeyCredentialCreationOptions::Parameters params; |
| params.type = PublicKeyCredentialType::PublicKey; |
| params.alg = -257; |
| |
| PublicKeyCredentialCreationOptions makeCredentialParam; |
| makeCredentialParam.rp = WTFMove(rp); |
| makeCredentialParam.user = WTFMove(user); |
| makeCredentialParam.pubKeyCredParams.append(WTFMove(params)); |
| |
| EXPECT_FALSE(isConvertibleToU2fRegisterCommand(makeCredentialParam)); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestU2fRegisterUserVerificationRequirement) |
| { |
| auto makeCredentialParam = constructMakeCredentialRequest(); |
| PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria selection; |
| selection.userVerification = UserVerificationRequirement::Required; |
| makeCredentialParam.authenticatorSelection = WTFMove(selection); |
| |
| EXPECT_FALSE(isConvertibleToU2fRegisterCommand(makeCredentialParam)); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestU2fRegisterResidentKeyRequirement) |
| { |
| auto makeCredentialParam = constructMakeCredentialRequest(); |
| PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria selection; |
| selection.requireResidentKey = true; |
| makeCredentialParam.authenticatorSelection = WTFMove(selection); |
| |
| EXPECT_FALSE(isConvertibleToU2fRegisterCommand(makeCredentialParam)); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestConvertCtapGetAssertionToU2fSignRequest) |
| { |
| auto getAssertionReq = constructGetAssertionRequest(); |
| PublicKeyCredentialDescriptor credentialDescriptor; |
| credentialDescriptor.type = PublicKeyCredentialType::PublicKey; |
| credentialDescriptor.id = WebCore::toBufferSource(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)); |
| Vector<PublicKeyCredentialDescriptor> allowedList; |
| allowedList.append(WTFMove(credentialDescriptor)); |
| getAssertionReq.allowCredentials = WTFMove(allowedList); |
| EXPECT_TRUE(isConvertibleToU2fSignCommand(getAssertionReq)); |
| |
| const auto u2fSignCommand = convertToU2fSignCommand(convertBytesToVector(TestData::kClientDataHash, sizeof(TestData::kClientDataHash)), getAssertionReq, WebCore::toBufferSource(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle))); |
| ASSERT_TRUE(u2fSignCommand); |
| EXPECT_EQ(*u2fSignCommand, convertBytesToVector(TestData::kU2fSignCommandApdu, sizeof(TestData::kU2fSignCommandApdu))); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestConvertCtapGetAssertionWithAppIDToU2fSignRequest) |
| { |
| auto getAssertionReq = constructGetAssertionRequest(); |
| PublicKeyCredentialDescriptor credentialDescriptor; |
| credentialDescriptor.type = PublicKeyCredentialType::PublicKey; |
| credentialDescriptor.id = WebCore::toBufferSource(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)); |
| Vector<PublicKeyCredentialDescriptor> allowedList; |
| allowedList.append(WTFMove(credentialDescriptor)); |
| getAssertionReq.allowCredentials = WTFMove(allowedList); |
| EXPECT_TRUE(isConvertibleToU2fSignCommand(getAssertionReq)); |
| |
| // AppID |
| WebCore::AuthenticationExtensionsClientInputs extensions; |
| extensions.appid = "https://www.example.com/appid"_s; |
| getAssertionReq.extensions = WTFMove(extensions); |
| |
| const auto u2fSignCommand = convertToU2fSignCommand(convertBytesToVector(TestData::kClientDataHash, sizeof(TestData::kClientDataHash)), getAssertionReq, WebCore::toBufferSource(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)), true); |
| ASSERT_TRUE(u2fSignCommand); |
| EXPECT_EQ(*u2fSignCommand, convertBytesToVector(TestData::kU2fAppIDSignCommandApdu, sizeof(TestData::kU2fAppIDSignCommandApdu))); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestU2fSignAllowListRequirement) |
| { |
| auto getAssertionReq = constructGetAssertionRequest(); |
| EXPECT_FALSE(isConvertibleToU2fSignCommand(getAssertionReq)); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestU2fSignUserVerificationRequirement) |
| { |
| auto getAssertionReq = constructGetAssertionRequest(); |
| PublicKeyCredentialDescriptor credentialDescriptor; |
| credentialDescriptor.type = PublicKeyCredentialType::PublicKey; |
| credentialDescriptor.id = WebCore::toBufferSource(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)); |
| Vector<PublicKeyCredentialDescriptor> allowedList; |
| allowedList.append(WTFMove(credentialDescriptor)); |
| getAssertionReq.allowCredentials = WTFMove(allowedList); |
| getAssertionReq.userVerification = UserVerificationRequirement::Required; |
| |
| EXPECT_FALSE(isConvertibleToU2fSignCommand(getAssertionReq)); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestCreateSignWithIncorrectKeyHandle) |
| { |
| auto getAssertionReq = constructGetAssertionRequest(); |
| PublicKeyCredentialDescriptor credentialDescriptor; |
| credentialDescriptor.type = PublicKeyCredentialType::PublicKey; |
| credentialDescriptor.id = WebCore::toBufferSource(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)); |
| Vector<PublicKeyCredentialDescriptor> allowedList; |
| allowedList.append(WTFMove(credentialDescriptor)); |
| getAssertionReq.allowCredentials = WTFMove(allowedList); |
| ASSERT_TRUE(isConvertibleToU2fSignCommand(getAssertionReq)); |
| |
| Vector<uint8_t> keyHandle(kMaxKeyHandleLength, 0xff); |
| const auto validSignCommand = convertToU2fSignCommand(convertBytesToVector(TestData::kClientDataHash, sizeof(TestData::kClientDataHash)), getAssertionReq, WebCore::toBufferSource(keyHandle.data(), keyHandle.size())); |
| EXPECT_TRUE(validSignCommand); |
| |
| keyHandle.append(0xff); |
| const auto invalidSignCommand = convertToU2fSignCommand(convertBytesToVector(TestData::kClientDataHash, sizeof(TestData::kClientDataHash)), getAssertionReq, WebCore::toBufferSource(keyHandle.data(), keyHandle.size())); |
| EXPECT_FALSE(invalidSignCommand); |
| } |
| |
| TEST(U2fCommandConstructorTest, TestConstructBogusU2fRegistrationCommand) |
| { |
| EXPECT_EQ(constructBogusU2fRegistrationCommand(), convertBytesToVector(TestData::kU2fFakeRegisterCommand, sizeof(TestData::kU2fFakeRegisterCommand))); |
| } |
| |
| } // namespace TestWebKitAPI |
| |
| #endif // ENABLE(WEB_AUTHN) |