| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Copyright (C) 2018-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 <JavaScriptCore/ArrayBuffer.h> |
| #include <WebCore/AuthenticatorAttachment.h> |
| #include <WebCore/AuthenticatorTransport.h> |
| #include <WebCore/BufferSource.h> |
| #include <WebCore/CBORReader.h> |
| #include <WebCore/CBORValue.h> |
| #include <WebCore/CBORWriter.h> |
| #include <WebCore/DeviceResponseConverter.h> |
| #include <WebCore/FidoConstants.h> |
| #include <WebCore/U2fResponseConverter.h> |
| #include <WebCore/WebAuthenticationUtils.h> |
| |
| namespace TestWebKitAPI { |
| using namespace WebCore; |
| using namespace fido; |
| |
| constexpr uint8_t kTestAuthenticatorGetInfoResponseWithNoVersion[] = { |
| // Success status byte |
| 0x00, |
| // Map of 6 elements |
| 0xA6, |
| // Key(01) - versions |
| 0x01, |
| // Array(0) |
| 0x80, |
| // Key(02) - extensions |
| 0x02, |
| // Array(2) |
| 0x82, |
| // "uvm" |
| 0x63, 0x75, 0x76, 0x6D, |
| // "hmac-secret" |
| 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, |
| // Key(03) - AAGUID |
| 0x03, |
| // Bytes(16) |
| 0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17, |
| 0x11, 0x1F, 0x9E, 0xDC, 0x7D, |
| // Key(04) - options |
| 0x04, |
| // Map(05) |
| 0xA5, |
| // Key - "rk" |
| 0x62, 0x72, 0x6B, |
| // true |
| 0xF5, |
| // Key - "up" |
| 0x62, 0x75, 0x70, |
| // true |
| 0xF5, |
| // Key - "uv" |
| 0x62, 0x75, 0x76, |
| // true |
| 0xF5, |
| // Key - "plat" |
| 0x64, 0x70, 0x6C, 0x61, 0x74, |
| // true |
| 0xF5, |
| // Key - "clientPin" |
| 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, |
| // false |
| 0xF4, |
| // Key(05) - Max message size |
| 0x05, |
| // 1200 |
| 0x19, 0x04, 0xB0, |
| // Key(06) - Pin protocols |
| 0x06, |
| // Array[1] |
| 0x81, 0x01, |
| }; |
| |
| constexpr uint8_t kTestAuthenticatorGetInfoResponseWithDuplicateVersion[] = { |
| // Success status byte |
| 0x00, |
| // Map of 6 elements |
| 0xA6, |
| // Key(01) - versions |
| 0x01, |
| // Array(02) |
| 0x82, |
| // "U2F_V2" |
| 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32, |
| // "U2F_V2" |
| 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32, |
| // Key(02) - extensions |
| 0x02, |
| // Array(2) |
| 0x82, |
| // "uvm" |
| 0x63, 0x75, 0x76, 0x6D, |
| // "hmac-secret" |
| 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, |
| // Key(03) - AAGUID |
| 0x03, |
| // Bytes(16) |
| 0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17, |
| 0x11, 0x1F, 0x9E, 0xDC, 0x7D, |
| // Key(04) - options |
| 0x04, |
| // Map(05) |
| 0xA5, |
| // Key - "rk" |
| 0x62, 0x72, 0x6B, |
| // true |
| 0xF5, |
| // Key - "up" |
| 0x62, 0x75, 0x70, |
| // true |
| 0xF5, |
| // Key - "uv" |
| 0x62, 0x75, 0x76, |
| // true |
| 0xF5, |
| // Key - "plat" |
| 0x64, 0x70, 0x6C, 0x61, 0x74, |
| // true |
| 0xF5, |
| // Key - "clientPin" |
| 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, |
| // false |
| 0xF4, |
| // Key(05) - Max message size |
| 0x05, |
| // 1200 |
| 0x19, 0x04, 0xB0, |
| // Key(06) - Pin protocols |
| 0x06, |
| // Array[1] |
| 0x81, 0x01, |
| }; |
| |
| constexpr uint8_t kTestAuthenticatorGetInfoResponseWithIncorrectAaguid[] = { |
| // Success status byte |
| 0x00, |
| // Map of 6 elements |
| 0xA6, |
| // Key(01) - versions |
| 0x01, |
| // Array(01) |
| 0x81, |
| // "U2F_V2" |
| 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32, |
| // Key(02) - extensions |
| 0x02, |
| // Array(2) |
| 0x82, |
| // "uvm" |
| 0x63, 0x75, 0x76, 0x6D, |
| // "hmac-secret" |
| 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, |
| // Key(03) - AAGUID |
| 0x03, |
| // Bytes(17) - FIDO2 device AAGUID must be 16 bytes long in order to be |
| // correct. |
| 0x51, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17, |
| 0x11, 0x1F, 0x9E, 0xDC, 0x7D, 0x00, |
| // Key(04) - options |
| 0x04, |
| // Map(05) |
| 0xA5, |
| // Key - "rk" |
| 0x62, 0x72, 0x6B, |
| // true |
| 0xF5, |
| // Key - "up" |
| 0x62, 0x75, 0x70, |
| // true |
| 0xF5, |
| // Key - "uv" |
| 0x62, 0x75, 0x76, |
| // true |
| 0xF5, |
| // Key - "plat" |
| 0x64, 0x70, 0x6C, 0x61, 0x74, |
| // true |
| 0xF5, |
| // Key - "clientPin" |
| 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, |
| // false |
| 0xF4, |
| // Key(05) - Max message size |
| 0x05, |
| // 1200 |
| 0x19, 0x04, 0xB0, |
| // Key(06) - Pin protocols |
| 0x06, |
| // Array[1] |
| 0x81, 0x01, |
| }; |
| |
| // The attested credential data, excluding the public key bytes. Append |
| // with kTestECPublicKeyCOSE to get the complete attestation data. |
| constexpr uint8_t kTestAttestedCredentialDataPrefix[] = { |
| // 16-byte aaguid |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| // 2-byte length |
| 0x00, 0x40, |
| // 64-byte key handle |
| 0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26, |
| 0x35, 0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3, |
| 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94, |
| 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD, 0x39, 0x6B, 0x64, |
| 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08, |
| 0xFE, 0x42, 0x00, 0x38, |
| }; |
| |
| // The authenticator data, excluding the attested credential data bytes. Append |
| // with attested credential data to get the complete authenticator data. |
| constexpr uint8_t kTestAuthenticatorDataPrefix[] = { |
| // sha256 hash of rp id. |
| 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, |
| 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, |
| 0x01, 0x3D, 0xE9, 0x6D, 0x4E, 0xFB, 0x17, 0xDE, |
| // flags (TUP and AT bits set) |
| 0x41, |
| // counter |
| 0x00, 0x00, 0x00, 0x00 |
| }; |
| |
| // Components of the CBOR needed to form an authenticator object. |
| // Combined diagnostic notation: |
| // {"fmt": "fido-u2f", "attStmt": {"sig": h'30...}, "authData": h'D4C9D9...'} |
| constexpr uint8_t kFormatFidoU2fCBOR[] = { |
| // map(3) |
| 0xA3, |
| // text(3) |
| 0x63, |
| // "fmt" |
| 0x66, 0x6D, 0x74, |
| // text(8) |
| 0x68, |
| // "fido-u2f" |
| 0x66, 0x69, 0x64, 0x6F, 0x2D, 0x75, 0x32, 0x66 |
| }; |
| |
| constexpr uint8_t kAttStmtCBOR[] = { |
| // text(7) |
| 0x67, |
| // "attStmt" |
| 0x61, 0x74, 0x74, 0x53, 0x74, 0x6D, 0x74 |
| }; |
| |
| constexpr uint8_t kAuthDataCBOR[] = { |
| // text(8) |
| 0x68, |
| // "authData" |
| 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, |
| // bytes(196). i.e., the authenticator_data byte array corresponding to |
| // kTestAuthenticatorDataPrefix|, |kTestAttestedCredentialDataPrefix|, |
| // and test_data::kTestECPublicKeyCOSE. |
| 0x58, 0xC4 |
| }; |
| |
| constexpr uint8_t kTestDeviceAaguid[] = { |
| 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1F, 0x9E, 0xDC, 0x7D |
| }; |
| |
| Vector<uint8_t> getTestAttestedCredentialDataBytes() |
| { |
| // Combine kTestAttestedCredentialDataPrefix and kTestECPublicKeyCOSE. |
| auto testAttestedData = convertBytesToVector(kTestAttestedCredentialDataPrefix, sizeof(kTestAttestedCredentialDataPrefix)); |
| testAttestedData.append(TestData::kTestECPublicKeyCOSE, sizeof(TestData::kTestECPublicKeyCOSE)); |
| return testAttestedData; |
| } |
| |
| Vector<uint8_t> getTestAuthenticatorDataBytes() |
| { |
| // Build the test authenticator data. |
| auto testAuthenticatorData = convertBytesToVector(kTestAuthenticatorDataPrefix, sizeof(kTestAuthenticatorDataPrefix)); |
| auto testAttestedData = getTestAttestedCredentialDataBytes(); |
| testAuthenticatorData.appendVector(testAttestedData); |
| return testAuthenticatorData; |
| } |
| |
| Vector<uint8_t> getTestAttestationObjectBytes() |
| { |
| auto testAuthenticatorObject = convertBytesToVector(kFormatFidoU2fCBOR, sizeof(kFormatFidoU2fCBOR)); |
| testAuthenticatorObject.append(kAttStmtCBOR, sizeof(kAttStmtCBOR)); |
| testAuthenticatorObject.append(TestData::kU2fAttestationStatementCBOR, sizeof(TestData::kU2fAttestationStatementCBOR)); |
| testAuthenticatorObject.append(kAuthDataCBOR, sizeof(kAuthDataCBOR)); |
| auto testAuthenticatorData = getTestAuthenticatorDataBytes(); |
| testAuthenticatorObject.appendVector(testAuthenticatorData); |
| return testAuthenticatorObject; |
| } |
| |
| Vector<uint8_t> getTestSignResponse() |
| { |
| return convertBytesToVector(TestData::kTestU2fSignResponse, sizeof(TestData::kTestU2fSignResponse)); |
| } |
| |
| // Get a subset of the response for testing error handling. |
| Vector<uint8_t> getTestCorruptedSignResponse(size_t length) |
| { |
| ASSERT(length < sizeof(TestData::kTestU2fSignResponse)); |
| Vector<uint8_t> testCorruptedSignResponse; |
| testCorruptedSignResponse.reserveInitialCapacity(length); |
| testCorruptedSignResponse.append(TestData::kTestU2fSignResponse, length); |
| return testCorruptedSignResponse; |
| } |
| |
| // Return a key handle used for GetAssertion request. |
| BufferSource getTestCredentialRawIdBytes() |
| { |
| return WebCore::toBufferSource(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)); |
| } |
| |
| // Return a malformed U2fRegisterResponse. |
| Vector<uint8_t> getTestU2fRegisterResponse(size_t prefixSize, const uint8_t appendix[], size_t appendixSize) |
| { |
| Vector<uint8_t> result; |
| result.reserveInitialCapacity(prefixSize + appendixSize); |
| result.append(TestData::kTestU2fRegisterResponse, prefixSize); |
| result.append(appendix, appendixSize); |
| return result; |
| } |
| |
| // Leveraging example 4 of section 6.1 of the spec |
| // https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#commands |
| TEST(CTAPResponseTest, TestReadMakeCredentialResponse) |
| { |
| auto makeCredentialResponse = readCTAPMakeCredentialResponse(convertBytesToVector(TestData::kTestMakeCredentialResponse, sizeof(TestData::kTestMakeCredentialResponse)), AuthenticatorAttachment::CrossPlatform, { }); |
| ASSERT_TRUE(makeCredentialResponse); |
| auto cborAttestationObject = cbor::CBORReader::read(convertArrayBufferToVector(makeCredentialResponse->attestationObject())); |
| ASSERT_TRUE(cborAttestationObject); |
| ASSERT_TRUE(cborAttestationObject->isMap()); |
| |
| const auto& attestationObjectMap = cborAttestationObject->getMap(); |
| auto it = attestationObjectMap.find(cbor::CBORValue(kFormatKey)); |
| ASSERT_TRUE(it != attestationObjectMap.end()); |
| ASSERT_TRUE(it->second.isString()); |
| EXPECT_STREQ(it->second.getString().utf8().data(), "packed"); |
| |
| it = attestationObjectMap.find(cbor::CBORValue(kAuthDataKey)); |
| ASSERT_TRUE(it != attestationObjectMap.end()); |
| ASSERT_TRUE(it->second.isByteString()); |
| EXPECT_EQ(it->second.getByteString(), convertBytesToVector(TestData::kCtap2MakeCredentialAuthData, sizeof(TestData::kCtap2MakeCredentialAuthData))); |
| |
| it = attestationObjectMap.find(cbor::CBORValue(kAttestationStatementKey)); |
| ASSERT_TRUE(it != attestationObjectMap.end()); |
| ASSERT_TRUE(it->second.isMap()); |
| |
| const auto& attestationStatementMap = it->second.getMap(); |
| auto attStmtIt = attestationStatementMap.find(cbor::CBORValue("alg")); |
| |
| ASSERT_TRUE(attStmtIt != attestationStatementMap.end()); |
| ASSERT_TRUE(attStmtIt->second.isInteger()); |
| EXPECT_EQ(attStmtIt->second.getInteger(), -7); |
| |
| attStmtIt = attestationStatementMap.find(cbor::CBORValue("sig")); |
| ASSERT_TRUE(attStmtIt != attestationStatementMap.end()); |
| ASSERT_TRUE(attStmtIt->second.isByteString()); |
| EXPECT_EQ(attStmtIt->second.getByteString(), convertBytesToVector(TestData::kCtap2MakeCredentialSignature, sizeof(TestData::kCtap2MakeCredentialSignature))); |
| |
| attStmtIt = attestationStatementMap.find(cbor::CBORValue("x5c")); |
| ASSERT_TRUE(attStmtIt != attestationStatementMap.end()); |
| const auto& certificate = attStmtIt->second; |
| ASSERT_TRUE(certificate.isArray()); |
| ASSERT_EQ(certificate.getArray().size(), 1u); |
| ASSERT_TRUE(certificate.getArray()[0].isByteString()); |
| EXPECT_EQ(certificate.getArray()[0].getByteString(), convertBytesToVector(TestData::kCtap2MakeCredentialCertificate, sizeof(TestData::kCtap2MakeCredentialCertificate))); |
| EXPECT_EQ(makeCredentialResponse->rawId()->byteLength(), sizeof(TestData::kCtap2MakeCredentialCredentialId)); |
| EXPECT_EQ(memcmp(makeCredentialResponse->rawId()->data(), TestData::kCtap2MakeCredentialCredentialId, sizeof(TestData::kCtap2MakeCredentialCredentialId)), 0); |
| } |
| |
| // Leveraging example 5 of section 6.1 of the CTAP spec. |
| // https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html |
| TEST(CTAPResponseTest, TestReadGetAssertionResponse1) |
| { |
| auto getAssertionResponse = readCTAPGetAssertionResponse(convertBytesToVector(TestData::kDeviceGetAssertionResponseShort, sizeof(TestData::kDeviceGetAssertionResponseShort)), AuthenticatorAttachment::CrossPlatform); |
| ASSERT_TRUE(getAssertionResponse); |
| |
| EXPECT_EQ(getAssertionResponse->authenticatorData()->byteLength(), sizeof(TestData::kCtap2GetAssertionAuthData)); |
| EXPECT_EQ(memcmp(getAssertionResponse->authenticatorData()->data(), TestData::kCtap2GetAssertionAuthData, sizeof(TestData::kCtap2GetAssertionAuthData)), 0); |
| EXPECT_EQ(getAssertionResponse->signature()->byteLength(), sizeof(TestData::kCtap2GetAssertionSignature)); |
| EXPECT_EQ(memcmp(getAssertionResponse->signature()->data(), TestData::kCtap2GetAssertionSignature, sizeof(TestData::kCtap2GetAssertionSignature)), 0); |
| } |
| |
| TEST(CTAPResponseTest, TestReadGetAssertionResponse2) |
| { |
| auto getAssertionResponse = readCTAPGetAssertionResponse(convertBytesToVector(TestData::kDeviceGetAssertionResponse, sizeof(TestData::kDeviceGetAssertionResponse)), AuthenticatorAttachment::CrossPlatform); |
| ASSERT_TRUE(getAssertionResponse); |
| |
| EXPECT_EQ(getAssertionResponse->authenticatorData()->byteLength(), sizeof(TestData::kCtap2GetAssertionAuthData)); |
| EXPECT_EQ(memcmp(getAssertionResponse->authenticatorData()->data(), TestData::kCtap2GetAssertionAuthData, sizeof(TestData::kCtap2GetAssertionAuthData)), 0); |
| EXPECT_EQ(getAssertionResponse->signature()->byteLength(), sizeof(TestData::kCtap2GetAssertionSignature)); |
| EXPECT_EQ(memcmp(getAssertionResponse->signature()->data(), TestData::kCtap2GetAssertionSignature, sizeof(TestData::kCtap2GetAssertionSignature)), 0); |
| EXPECT_EQ(getAssertionResponse->userHandle()->byteLength(), sizeof(TestData::kCtap2GetAssertionUserHandle)); |
| EXPECT_EQ(memcmp(getAssertionResponse->userHandle()->data(), TestData::kCtap2GetAssertionUserHandle, sizeof(TestData::kCtap2GetAssertionUserHandle)), 0); |
| } |
| |
| TEST(CTAPResponseTest, TestReadGetAssertionResponse3) |
| { |
| auto getAssertionResponse = readCTAPGetAssertionResponse(convertBytesToVector(TestData::kDeviceGetAssertionResponseLong, sizeof(TestData::kDeviceGetAssertionResponseLong)), AuthenticatorAttachment::CrossPlatform); |
| ASSERT_TRUE(getAssertionResponse); |
| |
| EXPECT_EQ(getAssertionResponse->authenticatorData()->byteLength(), sizeof(TestData::kCtap2GetAssertionAuthData)); |
| EXPECT_EQ(memcmp(getAssertionResponse->authenticatorData()->data(), TestData::kCtap2GetAssertionAuthData, sizeof(TestData::kCtap2GetAssertionAuthData)), 0); |
| EXPECT_EQ(getAssertionResponse->signature()->byteLength(), sizeof(TestData::kCtap2GetAssertionSignature)); |
| EXPECT_EQ(memcmp(getAssertionResponse->signature()->data(), TestData::kCtap2GetAssertionSignature, sizeof(TestData::kCtap2GetAssertionSignature)), 0); |
| EXPECT_EQ(getAssertionResponse->userHandle()->byteLength(), sizeof(TestData::kCtap2GetAssertionUserHandle)); |
| EXPECT_EQ(memcmp(getAssertionResponse->userHandle()->data(), TestData::kCtap2GetAssertionUserHandle, sizeof(TestData::kCtap2GetAssertionUserHandle)), 0); |
| EXPECT_STREQ(getAssertionResponse->name().utf8().data(), "johnpsmith@example.com"); |
| EXPECT_STREQ(getAssertionResponse->displayName().utf8().data(), "John P. Smith"); |
| EXPECT_EQ(getAssertionResponse->numberOfCredentials(), 1u); |
| } |
| |
| // Test that U2F register response is properly parsed. |
| TEST(CTAPResponseTest, TestParseRegisterResponseData) |
| { |
| auto response = readU2fRegisterResponse(TestData::kRelyingPartyId, convertBytesToVector(TestData::kTestU2fRegisterResponse, sizeof(TestData::kTestU2fRegisterResponse)), AuthenticatorAttachment::CrossPlatform); |
| ASSERT_TRUE(response); |
| EXPECT_EQ(response->rawId()->byteLength(), sizeof(TestData::kU2fSignKeyHandle)); |
| EXPECT_EQ(memcmp(response->rawId()->data(), TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)), 0); |
| auto expectedAttestationObject = getTestAttestationObjectBytes(); |
| EXPECT_EQ(response->attestationObject()->byteLength(), expectedAttestationObject.size()); |
| EXPECT_EQ(memcmp(response->attestationObject()->data(), expectedAttestationObject.data(), expectedAttestationObject.size()), 0); |
| } |
| |
| // Test malformed user public key. |
| TEST(CTAPResponseTest, TestParseIncorrectRegisterResponseData1) |
| { |
| const uint8_t testData1[] = { 0x05 }; |
| auto response = readU2fRegisterResponse(TestData::kRelyingPartyId, convertBytesToVector(testData1, sizeof(testData1)), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| |
| const uint8_t testData2[] = { 0x05, 0x00 }; |
| response = readU2fRegisterResponse(TestData::kRelyingPartyId, convertBytesToVector(testData2, sizeof(testData2)), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| |
| const uint8_t testData3[] = { 0x05, 0x04, 0x00 }; |
| response = readU2fRegisterResponse(TestData::kRelyingPartyId, convertBytesToVector(testData3, sizeof(testData3)), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| } |
| |
| // Test malformed key handle. |
| TEST(CTAPResponseTest, TestParseIncorrectRegisterResponseData2) |
| { |
| auto response = readU2fRegisterResponse(TestData::kRelyingPartyId, getTestU2fRegisterResponse(kU2fKeyHandleLengthOffset, nullptr, 0), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| |
| const uint8_t testData[] = { 0x40 }; |
| response = readU2fRegisterResponse(TestData::kRelyingPartyId, getTestU2fRegisterResponse(kU2fKeyHandleLengthOffset, testData, sizeof(testData)), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| } |
| |
| // Test malformed X.509. |
| TEST(CTAPResponseTest, TestParseIncorrectRegisterResponseData3) |
| { |
| const auto prefix = kU2fKeyHandleOffset + 64; |
| auto response = readU2fRegisterResponse(TestData::kRelyingPartyId, getTestU2fRegisterResponse(prefix, nullptr, 0), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| |
| const uint8_t testData1[] = { 0x40 }; |
| response = readU2fRegisterResponse(TestData::kRelyingPartyId, getTestU2fRegisterResponse(prefix, testData1, sizeof(testData1)), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| |
| const uint8_t testData2[] = { 0x30 }; |
| response = readU2fRegisterResponse(TestData::kRelyingPartyId, getTestU2fRegisterResponse(prefix, testData2, sizeof(testData2)), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| |
| const uint8_t testData3[] = { 0x30, 0x82 }; |
| response = readU2fRegisterResponse(TestData::kRelyingPartyId, getTestU2fRegisterResponse(prefix, testData3, sizeof(testData3)), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| |
| const uint8_t testData4[] = { 0x30, 0xC1 }; |
| response = readU2fRegisterResponse(TestData::kRelyingPartyId, getTestU2fRegisterResponse(prefix, testData4, sizeof(testData4)), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| |
| const uint8_t testData5[] = { 0x30, 0x82, 0x02, 0x4A }; |
| response = readU2fRegisterResponse(TestData::kRelyingPartyId, getTestU2fRegisterResponse(prefix, testData5, sizeof(testData5)), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| } |
| |
| // Test malformed signature. |
| TEST(CTAPResponseTest, TestParseIncorrectRegisterResponseData4) |
| { |
| const auto prefix = sizeof(TestData::kTestU2fRegisterResponse); |
| auto response = readU2fRegisterResponse(TestData::kRelyingPartyId, getTestU2fRegisterResponse(prefix - 71, nullptr, 0), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| } |
| |
| // Test malformed X.509 but pass. |
| TEST(CTAPResponseTest, TestParseIncorrectRegisterResponseData5) |
| { |
| const auto prefix = kU2fKeyHandleOffset + 64; |
| const auto signatureSize = 71; |
| const auto suffix = sizeof(TestData::kTestU2fRegisterResponse) - signatureSize; |
| |
| Vector<uint8_t> testData1; |
| testData1.append(TestData::kTestU2fRegisterResponse, prefix); |
| testData1.append(0x30); |
| testData1.append(0x01); |
| testData1.append(0x00); |
| testData1.append(TestData::kTestU2fRegisterResponse + suffix, signatureSize); |
| auto response = readU2fRegisterResponse(TestData::kRelyingPartyId, testData1, AuthenticatorAttachment::CrossPlatform); |
| EXPECT_TRUE(response); |
| |
| Vector<uint8_t> testData2; |
| testData2.append(TestData::kTestU2fRegisterResponse, prefix); |
| testData2.append(0x30); |
| testData2.append(0x81); |
| testData2.append(0x01); |
| testData2.append(0x00); |
| testData2.append(TestData::kTestU2fRegisterResponse + suffix, signatureSize); |
| response = readU2fRegisterResponse(TestData::kRelyingPartyId, testData2, AuthenticatorAttachment::CrossPlatform); |
| EXPECT_TRUE(response); |
| } |
| |
| // Tests that U2F authenticator data is properly serialized. |
| TEST(CTAPResponseTest, TestParseSignResponseData) |
| { |
| auto response = readU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), getTestSignResponse(), AuthenticatorAttachment::CrossPlatform); |
| ASSERT_TRUE(response); |
| EXPECT_EQ(response->rawId()->byteLength(), sizeof(TestData::kU2fSignKeyHandle)); |
| EXPECT_EQ(memcmp(response->rawId()->data(), TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)), 0); |
| EXPECT_EQ(response->authenticatorData()->byteLength(), sizeof(TestData::kTestSignAuthenticatorData)); |
| EXPECT_EQ(memcmp(response->authenticatorData()->data(), TestData::kTestSignAuthenticatorData, sizeof(TestData::kTestSignAuthenticatorData)), 0); |
| EXPECT_EQ(response->signature()->byteLength(), sizeof(TestData::kU2fSignature)); |
| EXPECT_EQ(memcmp(response->signature()->data(), TestData::kU2fSignature, sizeof(TestData::kU2fSignature)), 0); |
| } |
| |
| TEST(CTAPResponseTest, TestParseU2fSignWithNullKeyHandle) |
| { |
| auto response = readU2fSignResponse(TestData::kRelyingPartyId, BufferSource(), getTestSignResponse(), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| } |
| |
| TEST(CTAPResponseTest, TestParseU2fSignWithNullResponse) |
| { |
| auto response = readU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), Vector<uint8_t>(), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| } |
| |
| TEST(CTAPResponseTest, TestParseU2fSignWithCorruptedCounter) |
| { |
| // A sign response of less than 5 bytes. |
| auto response = readU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), getTestCorruptedSignResponse(3), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| } |
| |
| TEST(CTAPResponseTest, TestParseU2fSignWithCorruptedSignature) |
| { |
| // A sign response no more than 5 bytes. |
| auto response = readU2fSignResponse(TestData::kRelyingPartyId, getTestCredentialRawIdBytes(), getTestCorruptedSignResponse(5), AuthenticatorAttachment::CrossPlatform); |
| EXPECT_FALSE(response); |
| } |
| |
| TEST(CTAPResponseTest, TestReadGetInfoResponse) |
| { |
| auto getInfoResponse = readCTAPGetInfoResponse(convertBytesToVector(TestData::kTestGetInfoResponsePlatformDevice, sizeof(TestData::kTestGetInfoResponsePlatformDevice))); |
| ASSERT_TRUE(getInfoResponse); |
| ASSERT_TRUE(getInfoResponse->maxMsgSize()); |
| EXPECT_EQ(*getInfoResponse->maxMsgSize(), 1200u); |
| EXPECT_NE(getInfoResponse->versions().find(ProtocolVersion::kCtap), getInfoResponse->versions().end()); |
| EXPECT_NE(getInfoResponse->versions().find(ProtocolVersion::kU2f), getInfoResponse->versions().end()); |
| EXPECT_TRUE(getInfoResponse->options().isPlatformDevice()); |
| EXPECT_EQ(AuthenticatorSupportedOptions::ResidentKeyAvailability::kSupported, getInfoResponse->options().residentKeyAvailability()); |
| EXPECT_TRUE(getInfoResponse->options().userPresenceRequired()); |
| EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedAndConfigured, getInfoResponse->options().userVerificationAvailability()); |
| EXPECT_EQ(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedButPinNotSet, getInfoResponse->options().clientPinAvailability()); |
| } |
| |
| TEST(CTAPResponseTest, TestReadGetInfoResponse2) |
| { |
| auto getInfoResponse = readCTAPGetInfoResponse(convertBytesToVector(TestData::kTestGetInfoResponsePlatformDevice2, sizeof(TestData::kTestGetInfoResponsePlatformDevice2))); |
| ASSERT_TRUE(getInfoResponse); |
| ASSERT_TRUE(getInfoResponse->maxMsgSize()); |
| EXPECT_EQ(*getInfoResponse->maxMsgSize(), 1200u); |
| EXPECT_NE(getInfoResponse->versions().find(ProtocolVersion::kCtap), getInfoResponse->versions().end()); |
| EXPECT_NE(getInfoResponse->versions().find(ProtocolVersion::kU2f), getInfoResponse->versions().end()); |
| EXPECT_TRUE(getInfoResponse->options().isPlatformDevice()); |
| EXPECT_EQ(AuthenticatorSupportedOptions::ResidentKeyAvailability::kSupported, getInfoResponse->options().residentKeyAvailability()); |
| EXPECT_TRUE(getInfoResponse->options().userPresenceRequired()); |
| EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedAndConfigured, getInfoResponse->options().userVerificationAvailability()); |
| EXPECT_EQ(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedButPinNotSet, getInfoResponse->options().clientPinAvailability()); |
| } |
| |
| TEST(CTAPResponseTest, TestReadGetInfoResponseDeviceYubikey5c) |
| { |
| auto getInfoResponse = readCTAPGetInfoResponse(convertBytesToVector(TestData::kTestGetInfoResponseDeviceYubikey5c, sizeof(TestData::kTestGetInfoResponseDeviceYubikey5c))); |
| ASSERT_TRUE(getInfoResponse); |
| ASSERT_TRUE(getInfoResponse->maxMsgSize()); |
| EXPECT_EQ(*getInfoResponse->maxMsgSize(), 1200u); |
| EXPECT_NE(getInfoResponse->versions().find(ProtocolVersion::kCtap), getInfoResponse->versions().end()); |
| EXPECT_NE(getInfoResponse->versions().find(ProtocolVersion::kU2f), getInfoResponse->versions().end()); |
| EXPECT_FALSE(getInfoResponse->options().isPlatformDevice()); |
| EXPECT_EQ(AuthenticatorSupportedOptions::ResidentKeyAvailability::kSupported, getInfoResponse->options().residentKeyAvailability()); |
| EXPECT_TRUE(getInfoResponse->options().userPresenceRequired()); |
| EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::kNotSupported, getInfoResponse->options().userVerificationAvailability()); |
| EXPECT_EQ(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet, getInfoResponse->options().clientPinAvailability()); |
| EXPECT_TRUE(getInfoResponse->transports()); |
| EXPECT_EQ(getInfoResponse->transports()->size(), 1u); |
| EXPECT_EQ(AuthenticatorTransport::Usb, getInfoResponse->transports()->first()); |
| } |
| |
| TEST(CTAPResponseTest, TestReadGetInfoResponseWithIncorrectFormat) |
| { |
| EXPECT_FALSE(readCTAPGetInfoResponse(convertBytesToVector(kTestAuthenticatorGetInfoResponseWithNoVersion, sizeof(kTestAuthenticatorGetInfoResponseWithNoVersion)))); |
| EXPECT_FALSE(readCTAPGetInfoResponse(convertBytesToVector(kTestAuthenticatorGetInfoResponseWithDuplicateVersion, sizeof(kTestAuthenticatorGetInfoResponseWithDuplicateVersion)))); |
| EXPECT_FALSE(readCTAPGetInfoResponse(convertBytesToVector(kTestAuthenticatorGetInfoResponseWithIncorrectAaguid, sizeof(kTestAuthenticatorGetInfoResponseWithIncorrectAaguid)))); |
| } |
| |
| TEST(CTAPResponseTest, TestSerializeGetInfoResponse) |
| { |
| AuthenticatorGetInfoResponse response({ ProtocolVersion::kCtap, ProtocolVersion::kU2f }, convertBytesToVector(kTestDeviceAaguid, sizeof(kTestDeviceAaguid))); |
| response.setExtensions({ "uvm"_s, "hmac-secret"_s }); |
| AuthenticatorSupportedOptions options; |
| options.setResidentKeyAvailability(AuthenticatorSupportedOptions::ResidentKeyAvailability::kSupported); |
| options.setIsPlatformDevice(true); |
| options.setClientPinAvailability(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedButPinNotSet); |
| options.setUserVerificationAvailability(AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedAndConfigured); |
| response.setOptions(WTFMove(options)); |
| response.setMaxMsgSize(1200); |
| response.setPinProtocols({ 1 }); |
| |
| auto responseAsCBOR = encodeAsCBOR(response); |
| EXPECT_EQ(responseAsCBOR.size(), sizeof(TestData::kTestGetInfoResponsePlatformDevice) - 1); |
| EXPECT_EQ(memcmp(responseAsCBOR.data(), TestData::kTestGetInfoResponsePlatformDevice + 1, responseAsCBOR.size()), 0); |
| } |
| |
| } // namespace TestWebKitAPI |
| |
| #endif // ENABLE(WEB_AUTHN) |