| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/webauth/authenticator_impl.h" |
| |
| #include <string> |
| |
| #include "base/base64url.h" |
| #include "base/run_loop.h" |
| #include "base/test/gtest_util.h" |
| #include "content/browser/webauth/attestation_object.h" |
| #include "content/browser/webauth/attested_credential_data.h" |
| #include "content/browser/webauth/authenticator_data.h" |
| #include "content/browser/webauth/authenticator_utils.h" |
| #include "content/browser/webauth/cbor/cbor_writer.h" |
| #include "content/browser/webauth/collected_client_data.h" |
| #include "content/browser/webauth/ec_public_key.h" |
| #include "content/browser/webauth/fido_attestation_statement.h" |
| #include "content/browser/webauth/register_response_data.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/navigation_simulator.h" |
| #include "content/public/test/test_renderer_host.h" |
| #include "content/test/test_render_frame_host.h" |
| #include "mojo/public/cpp/bindings/binding.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| using ::testing::_; |
| |
| using webauth::mojom::AuthenticatorPtr; |
| using webauth::mojom::AuthenticatorStatus; |
| using webauth::mojom::MakePublicKeyCredentialOptions; |
| using webauth::mojom::MakePublicKeyCredentialOptionsPtr; |
| using webauth::mojom::PublicKeyCredentialRpEntity; |
| using webauth::mojom::PublicKeyCredentialRpEntityPtr; |
| using webauth::mojom::PublicKeyCredentialUserEntity; |
| using webauth::mojom::PublicKeyCredentialUserEntityPtr; |
| using webauth::mojom::PublicKeyCredentialInfoPtr; |
| using webauth::mojom::PublicKeyCredentialParameters; |
| using webauth::mojom::PublicKeyCredentialParametersPtr; |
| |
| namespace { |
| |
| constexpr char kTestOrigin1[] = "https://google.com"; |
| |
| // Test data. CBOR test data can be built using the given diagnostic strings |
| // and the utility at "http://cbor.me/". |
| constexpr int32_t kCoseEs256 = -7; |
| |
| constexpr char kTestRelyingPartyId[] = "google.com"; |
| |
| constexpr uint8_t kTestChallengeBytes[] = { |
| 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, |
| 0x50, 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, |
| 0xB8, 0x8C, 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41}; |
| |
| constexpr char kTestClientDataJsonString[] = |
| "{\"challenge\":\"aHE0loIi7BcgLkJQX47SsWriLxa7BbiMJdueYCZF8UE\"," |
| "\"hashAlgorithm\"" |
| ":\"SHA-256\",\"origin\":\"google.com\",\"tokenBinding\":\"unused\"," |
| "\"type\":\"webauthn.create\"}"; |
| |
| constexpr uint8_t kTestCredentialRawIdBytes[] = { |
| 0x89, 0xAF, 0xB5, 0x24, 0x91, 0x1C, 0x40, 0x2B, 0x7F, 0x74, 0x59, |
| 0xC9, 0xF2, 0x21, 0xAF, 0xE6, 0xE5, 0x56, 0x65, 0x85, 0x04, 0xE8, |
| 0x5B, 0x49, 0x4D, 0x07, 0x55, 0x55, 0xF4, 0x6A, 0xBC, 0x44, 0x7B, |
| 0x15, 0xFC, 0x62, 0x61, 0x90, 0xA5, 0xFE, 0xEB, 0xE5, 0x9F, 0x5E, |
| 0xDC, 0x75, 0x32, 0x98, 0x6F, 0x44, 0x69, 0xD7, 0xF6, 0x13, 0xEB, |
| 0xAA, 0xEA, 0x33, 0xFB, 0xD5, 0x8E, 0xBF, 0xC6, 0x09}; |
| |
| // CBOR-encoded EC public key. |
| // Diagnostic notation: |
| // {"x": h'F868CE3869605224CE1059C0047EF01B830F2AD93BE27A3211F44E894560E695', |
| // "y": h'4E11538CABA2DF1CC1A6F250ED9F0C8B28B39DA44539DFABD46B589CD0E202E5', |
| // "alg": "ES256"} |
| constexpr uint8_t kTestECPublicKeyCBOR[] = { |
| // clang-format off |
| 0xA3, // map(3) |
| 0x61, // text(1) |
| 0x78, // "x" |
| 0x58, 0x20, // bytes(32) |
| 0xF8, 0x68, 0xCE, 0x38, 0x69, 0x60, 0x52, 0x24, 0xCE, 0x10, 0x59, 0xC0, |
| 0x04, 0x7E, 0xF0, 0x1B, 0x83, 0x0F, 0x2A, 0xD9, 0x3B, 0xE2, 0x7A, 0x32, |
| 0x11, 0xF4, 0x4E, 0x89, 0x45, 0x60, 0xE6, 0x95, |
| 0x61, // text(1) |
| 0x79, // "y" |
| 0x58, 0x20, // bytes(32) |
| 0x4E, 0x11, 0x53, 0x8C, 0xAB, 0xA2, 0xDF, 0x1C, 0xC1, 0xA6, 0xF2, 0x50, |
| 0xED, 0x9F, 0x0C, 0x8B, 0x28, 0xB3, 0x9D, 0xA4, 0x45, 0x39, 0xDF, 0xAB, |
| 0xD4, 0x6B, 0x58, 0x9C, 0xD0, 0xE2, 0x02, 0xE5, |
| 0x63, // text(3) |
| 0x61, 0x6C, 0x67, // "alg" |
| 0x65, // text(5) |
| 0x45, 0x53, 0x32, 0x35, 0x36, // "ES256" |
| // clang-format on |
| }; |
| |
| // U2F response blob produced by a U2F registration request. This example |
| // data uses testClientDataJson and 'created' a key with testCredentialRawId |
| // as its key handle and with the above x- and y- coordinates. |
| constexpr uint8_t kTestU2fRegisterResponse[] = { |
| 0x05, 0x04, 0xF8, 0x68, 0xCE, 0x38, 0x69, 0x60, 0x52, 0x24, 0xCE, 0x10, |
| 0x59, 0xC0, 0x04, 0x7E, 0xF0, 0x1B, 0x83, 0x0F, 0x2A, 0xD9, 0x3B, 0xE2, |
| 0x7A, 0x32, 0x11, 0xF4, 0x4E, 0x89, 0x45, 0x60, 0xE6, 0x95, 0x4E, 0x11, |
| 0x53, 0x8C, 0xAB, 0xA2, 0xDF, 0x1C, 0xC1, 0xA6, 0xF2, 0x50, 0xED, 0x9F, |
| 0x0C, 0x8B, 0x28, 0xB3, 0x9D, 0xA4, 0x45, 0x39, 0xDF, 0xAB, 0xD4, 0x6B, |
| 0x58, 0x9C, 0xD0, 0xE2, 0x02, 0xE5, 0x40, 0x89, 0xAF, 0xB5, 0x24, 0x91, |
| 0x1C, 0x40, 0x2B, 0x7F, 0x74, 0x59, 0xC9, 0xF2, 0x21, 0xAF, 0xE6, 0xE5, |
| 0x56, 0x65, 0x85, 0x04, 0xE8, 0x5B, 0x49, 0x4D, 0x07, 0x55, 0x55, 0xF4, |
| 0x6A, 0xBC, 0x44, 0x7B, 0x15, 0xFC, 0x62, 0x61, 0x90, 0xA5, 0xFE, 0xEB, |
| 0xE5, 0x9F, 0x5E, 0xDC, 0x75, 0x32, 0x98, 0x6F, 0x44, 0x69, 0xD7, 0xF6, |
| 0x13, 0xEB, 0xAA, 0xEA, 0x33, 0xFB, 0xD5, 0x8E, 0xBF, 0xC6, 0x09, 0x30, |
| 0x82, 0x02, 0x4A, 0x30, 0x82, 0x01, 0x32, 0xA0, 0x03, 0x02, 0x01, 0x02, |
| 0x02, 0x04, 0x04, 0x6C, 0x88, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, |
| 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x2E, 0x31, |
| 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x59, 0x75, |
| 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x52, 0x6F, 0x6F, |
| 0x74, 0x20, 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, |
| 0x34, 0x35, 0x37, 0x32, 0x30, 0x30, 0x36, 0x33, 0x31, 0x30, 0x20, 0x17, |
| 0x0D, 0x31, 0x34, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, |
| 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x35, 0x30, 0x30, 0x39, 0x30, 0x34, |
| 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x2C, 0x31, 0x2A, 0x30, |
| 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x21, 0x59, 0x75, 0x62, 0x69, |
| 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, 0x45, 0x20, 0x53, 0x65, |
| 0x72, 0x69, 0x61, 0x6C, 0x20, 0x32, 0x34, 0x39, 0x31, 0x38, 0x32, 0x33, |
| 0x32, 0x34, 0x37, 0x37, 0x30, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, |
| 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, |
| 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3C, 0xCA, 0xB9, 0x2C, |
| 0xCB, 0x97, 0x28, 0x7E, 0xE8, 0xE6, 0x39, 0x43, 0x7E, 0x21, 0xFC, 0xD6, |
| 0xB6, 0xF1, 0x65, 0xB2, 0xD5, 0xA3, 0xF3, 0xDB, 0x13, 0x1D, 0x31, 0xC1, |
| 0x6B, 0x74, 0x2B, 0xB4, 0x76, 0xD8, 0xD1, 0xE9, 0x90, 0x80, 0xEB, 0x54, |
| 0x6C, 0x9B, 0xBD, 0xF5, 0x56, 0xE6, 0x21, 0x0F, 0xD4, 0x27, 0x85, 0x89, |
| 0x9E, 0x78, 0xCC, 0x58, 0x9E, 0xBE, 0x31, 0x0F, 0x6C, 0xDB, 0x9F, 0xF4, |
| 0xA3, 0x3B, 0x30, 0x39, 0x30, 0x22, 0x06, 0x09, 0x2B, 0x06, 0x01, 0x04, |
| 0x01, 0x82, 0xC4, 0x0A, 0x02, 0x04, 0x15, 0x31, 0x2E, 0x33, 0x2E, 0x36, |
| 0x2E, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x34, 0x31, 0x34, 0x38, 0x32, |
| 0x2E, 0x31, 0x2E, 0x32, 0x30, 0x13, 0x06, 0x0B, 0x2B, 0x06, 0x01, 0x04, |
| 0x01, 0x82, 0xE5, 0x1C, 0x02, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x04, |
| 0x30, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, |
| 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9F, 0x9B, 0x05, |
| 0x22, 0x48, 0xBC, 0x4C, 0xF4, 0x2C, 0xC5, 0x99, 0x1F, 0xCA, 0xAB, 0xAC, |
| 0x9B, 0x65, 0x1B, 0xBE, 0x5B, 0xDC, 0xDC, 0x8E, 0xF0, 0xAD, 0x2C, 0x1C, |
| 0x1F, 0xFB, 0x36, 0xD1, 0x87, 0x15, 0xD4, 0x2E, 0x78, 0xB2, 0x49, 0x22, |
| 0x4F, 0x92, 0xC7, 0xE6, 0xE7, 0xA0, 0x5C, 0x49, 0xF0, 0xE7, 0xE4, 0xC8, |
| 0x81, 0xBF, 0x2E, 0x94, 0xF4, 0x5E, 0x4A, 0x21, 0x83, 0x3D, 0x74, 0x56, |
| 0x85, 0x1D, 0x0F, 0x6C, 0x14, 0x5A, 0x29, 0x54, 0x0C, 0x87, 0x4F, 0x30, |
| 0x92, 0xC9, 0x34, 0xB4, 0x3D, 0x22, 0x2B, 0x89, 0x62, 0xC0, 0xF4, 0x10, |
| 0xCE, 0xF1, 0xDB, 0x75, 0x89, 0x2A, 0xF1, 0x16, 0xB4, 0x4A, 0x96, 0xF5, |
| 0xD3, 0x5A, 0xDE, 0xA3, 0x82, 0x2F, 0xC7, 0x14, 0x6F, 0x60, 0x04, 0x38, |
| 0x5B, 0xCB, 0x69, 0xB6, 0x5C, 0x99, 0xE7, 0xEB, 0x69, 0x19, 0x78, 0x67, |
| 0x03, 0xC0, 0xD8, 0xCD, 0x41, 0xE8, 0xF7, 0x5C, 0xCA, 0x44, 0xAA, 0x8A, |
| 0xB7, 0x25, 0xAD, 0x8E, 0x79, 0x9F, 0xF3, 0xA8, 0x69, 0x6A, 0x6F, 0x1B, |
| 0x26, 0x56, 0xE6, 0x31, 0xB1, 0xE4, 0x01, 0x83, 0xC0, 0x8F, 0xDA, 0x53, |
| 0xFA, 0x4A, 0x8F, 0x85, 0xA0, 0x56, 0x93, 0x94, 0x4A, 0xE1, 0x79, 0xA1, |
| 0x33, 0x9D, 0x00, 0x2D, 0x15, 0xCA, 0xBD, 0x81, 0x00, 0x90, 0xEC, 0x72, |
| 0x2E, 0xF5, 0xDE, 0xF9, 0x96, 0x5A, 0x37, 0x1D, 0x41, 0x5D, 0x62, 0x4B, |
| 0x68, 0xA2, 0x70, 0x7C, 0xAD, 0x97, 0xBC, 0xDD, 0x17, 0x85, 0xAF, 0x97, |
| 0xE2, 0x58, 0xF3, 0x3D, 0xF5, 0x6A, 0x03, 0x1A, 0xA0, 0x35, 0x6D, 0x8E, |
| 0x8D, 0x5E, 0xBC, 0xAD, 0xC7, 0x4E, 0x07, 0x16, 0x36, 0xC6, 0xB1, 0x10, |
| 0xAC, 0xE5, 0xCC, 0x9B, 0x90, 0xDF, 0xEA, 0xCA, 0xE6, 0x40, 0xFF, 0x1B, |
| 0xB0, 0xF1, 0xFE, 0x5D, 0xB4, 0xEF, 0xF7, 0xA9, 0x5F, 0x06, 0x07, 0x33, |
| 0xF5, 0x30, 0x44, 0x02, 0x20, 0x08, 0xC3, 0xF8, 0xDB, 0x6E, 0x29, 0xFD, |
| 0x8B, 0x14, 0xD9, 0xDE, 0x1B, 0xD9, 0x8E, 0x84, 0x07, 0x2C, 0xB8, 0x13, |
| 0x38, 0x59, 0x89, 0xAA, 0x2C, 0xA2, 0x89, 0x39, 0x5E, 0x00, 0x09, 0xB8, |
| 0xB7, 0x02, 0x20, 0x26, 0x07, 0xB4, 0xF9, 0xAD, 0x05, 0xDE, 0x26, 0xF5, |
| 0x6F, 0x48, 0xB8, 0x25, 0x69, 0xEA, 0xD8, 0x23, 0x1A, 0x5A, 0x6C, 0x3A, |
| 0x14, 0x48, 0xDE, 0xAA, 0xAF, 0x15, 0xC0, 0xEF, 0x29, 0x63, 0x1A}; |
| |
| // The attested credential data, excluding the CBOR public key bytes. Append |
| // with kTestECPublicKeyCBOR to get the complete attestation data. |
| constexpr uint8_t kTestAttestedCredentialDataPrefix[] = { |
| // clang-format off |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, // 16-byte aaguid |
| 0x00, 0x40, // 2-byte length |
| 0x89, 0xAF, 0xB5, 0x24, 0x91, 0x1C, 0x40, 0x2B, 0x7F, 0x74, 0x59, 0xC9, |
| 0xF2, 0x21, 0xAF, 0xE6, 0xE5, 0x56, 0x65, 0x85, 0x04, 0xE8, 0x5B, 0x49, |
| 0x4D, 0x07, 0x55, 0x55, 0xF4, 0x6A, 0xBC, 0x44, 0x7B, 0x15, 0xFC, 0x62, |
| 0x61, 0x90, 0xA5, 0xFE, 0xEB, 0xE5, 0x9F, 0x5E, 0xDC, 0x75, 0x32, 0x98, |
| 0x6F, 0x44, 0x69, 0xD7, 0xF6, 0x13, 0xEB, 0xAA, 0xEA, 0x33, 0xFB, 0xD5, |
| 0x8E, 0xBF, 0xC6, 0x09 //64-byte key handle |
| // clang-format on |
| }; |
| |
| // The authenticator data, excluding the attested credential data bytes. Append |
| // with attested credential data to get the complete authenticator data. |
| constexpr uint8_t kTestAuthenticatorDataPrefix[] = { |
| // clang-format off |
| // sha256 hash of kTestRelyingPartyId |
| 0xD4, 0xC9, 0xD9, 0x02, 0x73, 0x26, 0x27, 0x1A, 0x89, 0xCE, 0x51, |
| 0xFC, 0xAF, 0x32, 0x8E, 0xD6, 0x73, 0xF1, 0x7B, 0xE3, 0x34, 0x69, |
| 0xFF, 0x97, 0x9E, 0x8A, 0xB8, 0xDD, 0x50, 0x1E, 0x66, 0x4F, |
| 0x41, // flags (TUP and AT bits set) |
| 0x00, 0x00, 0x00, 0x00 //counter |
| // clang-format on |
| }; |
| |
| // The attestation statement, a CBOR-encoded byte array. |
| // Diagnostic notation: |
| // {"sig": |
| // h'3044022008C3F8DB6E29FD8B14D9DE1BD98E84072CB813385989AA2CA289395E0009B8B70 \ |
| // 2202607B4F9AD05DE26F56F48B82569EAD8231A5A6C3A1448DEAAAF15C0EF29631A', |
| // "x5c": [h'3082024A30820132A0030201020204046C8822300D06092A864886F70D01010B0 \ |
| // 500302E312C302A0603550403132359756269636F2055324620526F6F742043412053657269 \ |
| // 616C203435373230303633313020170D3134303830313030303030305A180F3230353030393 \ |
| // 0343030303030305A302C312A302806035504030C2159756269636F20553246204545205365 \ |
| // 7269616C203234393138323332343737303059301306072A8648CE3D020106082A8648CE3D0 \ |
| // 30107034200043CCAB92CCB97287EE8E639437E21FCD6B6F165B2D5A3F3DB131D31C16B742B \ |
| // B476D8D1E99080EB546C9BBDF556E6210FD42785899E78CC589EBE310F6CDB9FF4A33B30393 \ |
| // 02206092B0601040182C40A020415312E332E362E312E342E312E34313438322E312E323013 \ |
| // 060B2B0601040182E51C020101040403020430300D06092A864886F70D01010B05000382010 \ |
| // 1009F9B052248BC4CF42CC5991FCAABAC9B651BBE5BDCDC8EF0AD2C1C1FFB36D18715D42E78 \ |
| // B249224F92C7E6E7A05C49F0E7E4C881BF2E94F45E4A21833D7456851D0F6C145A29540C874 \ |
| // F3092C934B43D222B8962C0F410CEF1DB75892AF116B44A96F5D35ADEA3822FC7146F600438 \ |
| // 5BCB69B65C99E7EB6919786703C0D8CD41E8F75CCA44AA8AB725AD8E799FF3A8696A6F1B265 \ |
| // 6E631B1E40183C08FDA53FA4A8F85A05693944AE179A1339D002D15CABD810090EC722EF5DE \ |
| // F9965A371D415D624B68A2707CAD97BCDD1785AF97E258F33DF56A031AA0356D8E8D5EBCADC \ |
| // 74E071636C6B110ACE5CC9B90DFEACAE640FF1BB0F1FE5DB4EFF7A95F060733F5']} |
| constexpr uint8_t kU2fAttestationStatementCBOR[] = { |
| // clang-format off |
| 0xA2, // map(2) |
| 0x63, // text(3) |
| 0x73, 0x69, 0x67, // "sig" |
| 0x58, 0x46, // bytes(70) |
| 0x30, 0x44, 0x02, 0x20, 0x08, 0xC3, 0xF8, 0xDB, 0x6E, 0x29, 0xFD, 0x8B, |
| 0x14, 0xD9, 0xDE, 0x1B, 0xD9, 0x8E, 0x84, 0x07, 0x2C, 0xB8, 0x13, 0x38, |
| 0x59, 0x89, 0xAA, 0x2C, 0xA2, 0x89, 0x39, 0x5E, 0x00, 0x09, 0xB8, 0xB7, |
| 0x02, 0x20, 0x26, 0x07, 0xB4, 0xF9, 0xAD, 0x05, 0xDE, 0x26, 0xF5, 0x6F, |
| 0x48, 0xB8, 0x25, 0x69, 0xEA, 0xD8, 0x23, 0x1A, 0x5A, 0x6C, 0x3A, 0x14, |
| 0x48, 0xDE, 0xAA, 0xAF, 0x15, 0xC0, 0xEF, 0x29, 0x63, 0x1A, |
| 0x63, // text(3) |
| 0x78, 0x35, 0x63, // "x5c" |
| 0x81, // array(1) |
| 0x59, 0x02, 0x4E, // bytes(590) |
| 0x30, 0x82, 0x02, 0x4A, 0x30, 0x82, 0x01, 0x32, 0xA0, 0x03, 0x02, |
| 0x01, 0x02, 0x02, 0x04, 0x04, 0x6C, 0x88, 0x22, 0x30, 0x0D, 0x06, |
| 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, |
| 0x00, 0x30, 0x2E, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, |
| 0x03, 0x13, 0x23, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, |
| 0x32, 0x46, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, |
| 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, 0x34, 0x35, 0x37, 0x32, |
| 0x30, 0x30, 0x36, 0x33, 0x31, 0x30, 0x20, 0x17, 0x0D, 0x31, 0x34, |
| 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, |
| 0x18, 0x0F, 0x32, 0x30, 0x35, 0x30, 0x30, 0x39, 0x30, 0x34, 0x30, |
| 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x2C, 0x31, 0x2A, 0x30, |
| 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x21, 0x59, 0x75, 0x62, |
| 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, 0x45, 0x20, |
| 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, 0x32, 0x34, 0x39, 0x31, |
| 0x38, 0x32, 0x33, 0x32, 0x34, 0x37, 0x37, 0x30, 0x30, 0x59, 0x30, |
| 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, |
| 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, |
| 0x00, 0x04, 0x3C, 0xCA, 0xB9, 0x2C, 0xCB, 0x97, 0x28, 0x7E, 0xE8, |
| 0xE6, 0x39, 0x43, 0x7E, 0x21, 0xFC, 0xD6, 0xB6, 0xF1, 0x65, 0xB2, |
| 0xD5, 0xA3, 0xF3, 0xDB, 0x13, 0x1D, 0x31, 0xC1, 0x6B, 0x74, 0x2B, |
| 0xB4, 0x76, 0xD8, 0xD1, 0xE9, 0x90, 0x80, 0xEB, 0x54, 0x6C, 0x9B, |
| 0xBD, 0xF5, 0x56, 0xE6, 0x21, 0x0F, 0xD4, 0x27, 0x85, 0x89, 0x9E, |
| 0x78, 0xCC, 0x58, 0x9E, 0xBE, 0x31, 0x0F, 0x6C, 0xDB, 0x9F, 0xF4, |
| 0xA3, 0x3B, 0x30, 0x39, 0x30, 0x22, 0x06, 0x09, 0x2B, 0x06, 0x01, |
| 0x04, 0x01, 0x82, 0xC4, 0x0A, 0x02, 0x04, 0x15, 0x31, 0x2E, 0x33, |
| 0x2E, 0x36, 0x2E, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x34, 0x31, |
| 0x34, 0x38, 0x32, 0x2E, 0x31, 0x2E, 0x32, 0x30, 0x13, 0x06, 0x0B, |
| 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xE5, 0x1C, 0x02, 0x01, 0x01, |
| 0x04, 0x04, 0x03, 0x02, 0x04, 0x30, 0x30, 0x0D, 0x06, 0x09, 0x2A, |
| 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, |
| 0x82, 0x01, 0x01, 0x00, 0x9F, 0x9B, 0x05, 0x22, 0x48, 0xBC, 0x4C, |
| 0xF4, 0x2C, 0xC5, 0x99, 0x1F, 0xCA, 0xAB, 0xAC, 0x9B, 0x65, 0x1B, |
| 0xBE, 0x5B, 0xDC, 0xDC, 0x8E, 0xF0, 0xAD, 0x2C, 0x1C, 0x1F, 0xFB, |
| 0x36, 0xD1, 0x87, 0x15, 0xD4, 0x2E, 0x78, 0xB2, 0x49, 0x22, 0x4F, |
| 0x92, 0xC7, 0xE6, 0xE7, 0xA0, 0x5C, 0x49, 0xF0, 0xE7, 0xE4, 0xC8, |
| 0x81, 0xBF, 0x2E, 0x94, 0xF4, 0x5E, 0x4A, 0x21, 0x83, 0x3D, 0x74, |
| 0x56, 0x85, 0x1D, 0x0F, 0x6C, 0x14, 0x5A, 0x29, 0x54, 0x0C, 0x87, |
| 0x4F, 0x30, 0x92, 0xC9, 0x34, 0xB4, 0x3D, 0x22, 0x2B, 0x89, 0x62, |
| 0xC0, 0xF4, 0x10, 0xCE, 0xF1, 0xDB, 0x75, 0x89, 0x2A, 0xF1, 0x16, |
| 0xB4, 0x4A, 0x96, 0xF5, 0xD3, 0x5A, 0xDE, 0xA3, 0x82, 0x2F, 0xC7, |
| 0x14, 0x6F, 0x60, 0x04, 0x38, 0x5B, 0xCB, 0x69, 0xB6, 0x5C, 0x99, |
| 0xE7, 0xEB, 0x69, 0x19, 0x78, 0x67, 0x03, 0xC0, 0xD8, 0xCD, 0x41, |
| 0xE8, 0xF7, 0x5C, 0xCA, 0x44, 0xAA, 0x8A, 0xB7, 0x25, 0xAD, 0x8E, |
| 0x79, 0x9F, 0xF3, 0xA8, 0x69, 0x6A, 0x6F, 0x1B, 0x26, 0x56, 0xE6, |
| 0x31, 0xB1, 0xE4, 0x01, 0x83, 0xC0, 0x8F, 0xDA, 0x53, 0xFA, 0x4A, |
| 0x8F, 0x85, 0xA0, 0x56, 0x93, 0x94, 0x4A, 0xE1, 0x79, 0xA1, 0x33, |
| 0x9D, 0x00, 0x2D, 0x15, 0xCA, 0xBD, 0x81, 0x00, 0x90, 0xEC, 0x72, |
| 0x2E, 0xF5, 0xDE, 0xF9, 0x96, 0x5A, 0x37, 0x1D, 0x41, 0x5D, 0x62, |
| 0x4B, 0x68, 0xA2, 0x70, 0x7C, 0xAD, 0x97, 0xBC, 0xDD, 0x17, 0x85, |
| 0xAF, 0x97, 0xE2, 0x58, 0xF3, 0x3D, 0xF5, 0x6A, 0x03, 0x1A, 0xA0, |
| 0x35, 0x6D, 0x8E, 0x8D, 0x5E, 0xBC, 0xAD, 0xC7, 0x4E, 0x07, 0x16, |
| 0x36, 0xC6, 0xB1, 0x10, 0xAC, 0xE5, 0xCC, 0x9B, 0x90, 0xDF, 0xEA, |
| 0xCA, 0xE6, 0x40, 0xFF, 0x1B, 0xB0, 0xF1, 0xFE, 0x5D, 0xB4, 0xEF, |
| 0xF7, 0xA9, 0x5F, 0x06, 0x07, 0x33, 0xF5 |
| // clang-format on |
| }; |
| |
| // 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[] = { |
| // clang-format off |
| 0xA3, // map(3) |
| 0x63, // text(3) |
| 0x66, 0x6D, 0x74, // "fmt" |
| 0x68, // text(8) |
| 0x66, 0x69, 0x64, 0x6F, 0x2D, 0x75, 0x32, 0x66 // "fido-u2f" |
| // clang-format on |
| }; |
| |
| constexpr uint8_t kAttStmtCBOR[] = { |
| // clang-format off |
| 0x67, // text(7) |
| 0x61, 0x74, 0x74, 0x53, 0x74, 0x6D, 0x74 // "attStmt" |
| // clang-format on |
| }; |
| |
| constexpr uint8_t kAuthDataCBOR[] = { |
| // clang-format off |
| 0x68, // text(8) |
| 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, // "authData" |
| 0x58, 0xCA //bytes(202). i.e.,the authenticator_data bytearray |
| // clang-format on |
| }; |
| |
| // Helpers. |
| |
| const std::vector<uint8_t>& GetTestECPublicKeyCBOR() { |
| static const std::vector<uint8_t> data(std::begin(kTestECPublicKeyCBOR), |
| std::end(kTestECPublicKeyCBOR)); |
| return data; |
| } |
| |
| const std::vector<uint8_t>& GetTestRegisterResponse() { |
| static const std::vector<uint8_t> data(std::begin(kTestU2fRegisterResponse), |
| std::end(kTestU2fRegisterResponse)); |
| return data; |
| } |
| |
| const std::vector<uint8_t>& GetTestCredentialRawIdBytes() { |
| static const std::vector<uint8_t> data(std::begin(kTestCredentialRawIdBytes), |
| std::end(kTestCredentialRawIdBytes)); |
| return data; |
| } |
| |
| const std::vector<uint8_t>& GetTestChallengeBytes() { |
| static const std::vector<uint8_t> data(std::begin(kTestChallengeBytes), |
| std::end(kTestChallengeBytes)); |
| return data; |
| } |
| |
| const std::vector<uint8_t>& GetU2fAttestationStatementCBOR() { |
| static const std::vector<uint8_t> data( |
| std::begin(kU2fAttestationStatementCBOR), |
| std::end(kU2fAttestationStatementCBOR)); |
| return data; |
| } |
| |
| PublicKeyCredentialRpEntityPtr GetTestPublicKeyCredentialRPEntity() { |
| auto entity = PublicKeyCredentialRpEntity::New(); |
| entity->id = std::string("localhost"); |
| entity->name = std::string("TestRP@example.com"); |
| return entity; |
| } |
| |
| PublicKeyCredentialUserEntityPtr GetTestPublicKeyCredentialUserEntity() { |
| auto entity = PublicKeyCredentialUserEntity::New(); |
| entity->display_name = std::string("User A. Name"); |
| std::vector<uint8_t> id(32, 0x0A); |
| entity->id = id; |
| entity->name = std::string("username@example.com"); |
| entity->icon = GURL("fakeurl2.png"); |
| return entity; |
| } |
| |
| std::vector<PublicKeyCredentialParametersPtr> |
| GetTestPublicKeyCredentialParameters(int32_t algorithm_identifier) { |
| std::vector<PublicKeyCredentialParametersPtr> parameters; |
| auto fake_parameter = PublicKeyCredentialParameters::New(); |
| fake_parameter->type = webauth::mojom::PublicKeyCredentialType::PUBLIC_KEY; |
| fake_parameter->algorithm_identifier = algorithm_identifier; |
| parameters.push_back(std::move(fake_parameter)); |
| return parameters; |
| } |
| |
| MakePublicKeyCredentialOptionsPtr GetTestMakePublicKeyCredentialOptions() { |
| auto options = MakePublicKeyCredentialOptions::New(); |
| std::vector<uint8_t> buffer(32, 0x0A); |
| options->relying_party = GetTestPublicKeyCredentialRPEntity(); |
| options->user = GetTestPublicKeyCredentialUserEntity(); |
| options->public_key_parameters = |
| GetTestPublicKeyCredentialParameters(kCoseEs256); |
| options->challenge = std::move(buffer); |
| options->adjusted_timeout = base::TimeDelta::FromMinutes(1); |
| return options; |
| } |
| |
| std::unique_ptr<CollectedClientData> GetTestClientData(std::string type) { |
| std::unique_ptr<CollectedClientData> client_data = |
| CollectedClientData::Create(type, kTestRelyingPartyId, |
| GetTestChallengeBytes()); |
| return client_data; |
| } |
| |
| std::vector<uint8_t> GetTestAttestedCredentialDataBytes() { |
| // Combine kTestAttestedCredentialDataPrefix and kTestECPublicKeyCBOR. |
| std::vector<uint8_t> test_attested_data( |
| std::begin(kTestAttestedCredentialDataPrefix), |
| std::end(kTestAttestedCredentialDataPrefix)); |
| test_attested_data.insert(test_attested_data.end(), |
| std::begin(kTestECPublicKeyCBOR), |
| std::end(kTestECPublicKeyCBOR)); |
| return test_attested_data; |
| } |
| |
| std::vector<uint8_t> GetTestAuthenticatorDataBytes() { |
| // Build the test authenticator data. |
| std::vector<uint8_t> test_authenticator_data( |
| std::begin(kTestAuthenticatorDataPrefix), |
| std::end(kTestAuthenticatorDataPrefix)); |
| std::vector<uint8_t> test_attested_data = |
| GetTestAttestedCredentialDataBytes(); |
| test_authenticator_data.insert(test_authenticator_data.end(), |
| test_attested_data.begin(), |
| test_attested_data.end()); |
| return test_authenticator_data; |
| } |
| |
| std::vector<uint8_t> GetTestAttestationObjectBytes() { |
| std::vector<uint8_t> test_authenticator_object(std::begin(kFormatFidoU2fCBOR), |
| std::end(kFormatFidoU2fCBOR)); |
| test_authenticator_object.insert(test_authenticator_object.end(), |
| std::begin(kAttStmtCBOR), |
| std::end(kAttStmtCBOR)); |
| test_authenticator_object.insert(test_authenticator_object.end(), |
| std::begin(kU2fAttestationStatementCBOR), |
| std::end(kU2fAttestationStatementCBOR)); |
| test_authenticator_object.insert(test_authenticator_object.end(), |
| std::begin(kAuthDataCBOR), |
| std::end(kAuthDataCBOR)); |
| std::vector<uint8_t> test_authenticator_data = |
| GetTestAuthenticatorDataBytes(); |
| test_authenticator_object.insert(test_authenticator_object.end(), |
| test_authenticator_data.begin(), |
| test_authenticator_data.end()); |
| return test_authenticator_object; |
| } |
| |
| class AuthenticatorImplTest : public content::RenderViewHostTestHarness { |
| public: |
| AuthenticatorImplTest() {} |
| ~AuthenticatorImplTest() override {} |
| |
| protected: |
| // Simulates navigating to a page and getting the page contents and language |
| // for that navigation. |
| void SimulateNavigation(const GURL& url) { |
| if (main_rfh()->GetLastCommittedURL() != url) |
| NavigateAndCommit(url); |
| } |
| |
| AuthenticatorPtr ConnectToAuthenticator() { |
| authenticator_impl_.reset(new AuthenticatorImpl(main_rfh())); |
| AuthenticatorPtr authenticator; |
| authenticator_impl_->Bind(mojo::MakeRequest(&authenticator)); |
| return authenticator; |
| } |
| |
| private: |
| std::unique_ptr<AuthenticatorImpl> authenticator_impl_; |
| }; |
| |
| class TestMakeCredentialCallback { |
| public: |
| TestMakeCredentialCallback() |
| : callback_(base::Bind(&TestMakeCredentialCallback::ReceivedCallback, |
| base::Unretained(this))) {} |
| ~TestMakeCredentialCallback() {} |
| |
| void ReceivedCallback(AuthenticatorStatus status, |
| PublicKeyCredentialInfoPtr credential) { |
| response_ = std::make_pair(status, std::move(credential)); |
| closure_.Run(); |
| } |
| |
| std::pair<AuthenticatorStatus, PublicKeyCredentialInfoPtr>& |
| WaitForCallback() { |
| closure_ = run_loop_.QuitClosure(); |
| run_loop_.Run(); |
| return response_; |
| } |
| |
| const base::Callback<void(AuthenticatorStatus status, |
| PublicKeyCredentialInfoPtr credential)>& |
| callback() { |
| return callback_; |
| } |
| |
| private: |
| std::pair<AuthenticatorStatus, PublicKeyCredentialInfoPtr> response_; |
| base::Closure closure_; |
| base::Callback<void(AuthenticatorStatus status, |
| PublicKeyCredentialInfoPtr credential)> |
| callback_; |
| base::RunLoop run_loop_; |
| }; |
| |
| } // namespace |
| |
| // Test that service returns NOT_ALLOWED_ERROR on a call to MakeCredential with |
| // an opaque origin. |
| TEST_F(AuthenticatorImplTest, MakeCredentialOpaqueOrigin) { |
| NavigateAndCommit(GURL("data:text/html,opaque")); |
| AuthenticatorPtr authenticator = ConnectToAuthenticator(); |
| MakePublicKeyCredentialOptionsPtr options = |
| GetTestMakePublicKeyCredentialOptions(); |
| |
| TestMakeCredentialCallback cb; |
| authenticator->MakeCredential(std::move(options), cb.callback()); |
| std::pair<webauth::mojom::AuthenticatorStatus, |
| webauth::mojom::PublicKeyCredentialInfoPtr>& response = |
| cb.WaitForCallback(); |
| EXPECT_EQ(webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, |
| response.first); |
| } |
| |
| // Test that service returns NOT_SUPPORTED_ERROR if no parameters contain |
| // a supported algorithm. |
| TEST_F(AuthenticatorImplTest, MakeCredentialNoSupportedAlgorithm) { |
| SimulateNavigation(GURL(kTestOrigin1)); |
| AuthenticatorPtr authenticator = ConnectToAuthenticator(); |
| |
| MakePublicKeyCredentialOptionsPtr options = |
| GetTestMakePublicKeyCredentialOptions(); |
| options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); |
| |
| TestMakeCredentialCallback cb; |
| authenticator->MakeCredential(std::move(options), cb.callback()); |
| std::pair<webauth::mojom::AuthenticatorStatus, |
| webauth::mojom::PublicKeyCredentialInfoPtr>& response = |
| cb.WaitForCallback(); |
| EXPECT_EQ(webauth::mojom::AuthenticatorStatus::NOT_SUPPORTED_ERROR, |
| response.first); |
| } |
| |
| // Test that client data serializes to JSON properly. |
| TEST_F(AuthenticatorImplTest, TestSerializedClientData) { |
| EXPECT_EQ( |
| kTestClientDataJsonString, |
| GetTestClientData(authenticator_utils::kCreateType)->SerializeToJson()); |
| } |
| |
| // Test that an EC public key serializes to CBOR properly. |
| TEST_F(AuthenticatorImplTest, TestSerializedPublicKey) { |
| std::unique_ptr<ECPublicKey> public_key = |
| ECPublicKey::ExtractFromU2fRegistrationResponse( |
| authenticator_utils::kEs256, GetTestRegisterResponse()); |
| EXPECT_EQ(GetTestECPublicKeyCBOR(), public_key->EncodeAsCBOR()); |
| } |
| |
| // Test that the attestation statement cbor map is constructed properly. |
| TEST_F(AuthenticatorImplTest, TestU2fAttestationStatementCBOR) { |
| std::unique_ptr<FidoAttestationStatement> fido_attestation_statement = |
| FidoAttestationStatement::CreateFromU2fRegisterResponse( |
| GetTestRegisterResponse()); |
| auto cbor = |
| CBORWriter::Write(CBORValue(fido_attestation_statement->GetAsCBORMap())); |
| ASSERT_TRUE(cbor.has_value()); |
| EXPECT_EQ(GetU2fAttestationStatementCBOR(), cbor.value()); |
| } |
| |
| // Tests that well-formed attested credential data serializes properly. |
| TEST_F(AuthenticatorImplTest, TestAttestedCredentialData) { |
| std::unique_ptr<ECPublicKey> public_key = |
| ECPublicKey::ExtractFromU2fRegistrationResponse( |
| authenticator_utils::kEs256, GetTestRegisterResponse()); |
| std::unique_ptr<AttestedCredentialData> attested_data = |
| AttestedCredentialData::CreateFromU2fRegisterResponse( |
| GetTestRegisterResponse(), std::vector<uint8_t>(16, 0) /* aaguid */, |
| std::move(public_key)); |
| |
| EXPECT_EQ(GetTestAttestedCredentialDataBytes(), |
| attested_data->SerializeAsBytes()); |
| } |
| |
| // Tests that well-formed authenticator data serializes properly. |
| TEST_F(AuthenticatorImplTest, TestAuthenticatorData) { |
| std::unique_ptr<ECPublicKey> public_key = |
| ECPublicKey::ExtractFromU2fRegistrationResponse( |
| authenticator_utils::kEs256, GetTestRegisterResponse()); |
| std::unique_ptr<AttestedCredentialData> attested_data = |
| AttestedCredentialData::CreateFromU2fRegisterResponse( |
| GetTestRegisterResponse(), std::vector<uint8_t>(16, 0) /* aaguid */, |
| std::move(public_key)); |
| |
| AuthenticatorData::Flags flags = |
| static_cast<AuthenticatorData::Flags>( |
| AuthenticatorData::Flag::TEST_OF_USER_PRESENCE) | |
| static_cast<AuthenticatorData::Flags>( |
| AuthenticatorData::Flag::ATTESTATION); |
| |
| std::unique_ptr<AuthenticatorData> authenticator_data = |
| AuthenticatorData::Create( |
| GetTestClientData(authenticator_utils::kCreateType) |
| ->SerializeToJson(), |
| flags, std::vector<uint8_t>(4, 0) /* counter */, |
| std::move(attested_data)); |
| |
| EXPECT_EQ(GetTestAuthenticatorDataBytes(), |
| authenticator_data->SerializeToByteArray()); |
| } |
| |
| // Tests that a U2F attestation object serializes properly. |
| TEST_F(AuthenticatorImplTest, TestU2fAttestationObject) { |
| std::unique_ptr<ECPublicKey> public_key = |
| ECPublicKey::ExtractFromU2fRegistrationResponse( |
| authenticator_utils::kEs256, GetTestRegisterResponse()); |
| std::unique_ptr<AttestedCredentialData> attested_data = |
| AttestedCredentialData::CreateFromU2fRegisterResponse( |
| GetTestRegisterResponse(), std::vector<uint8_t>(16, 0) /* aaguid */, |
| std::move(public_key)); |
| |
| AuthenticatorData::Flags flags = |
| static_cast<AuthenticatorData::Flags>( |
| AuthenticatorData::Flag::TEST_OF_USER_PRESENCE) | |
| static_cast<AuthenticatorData::Flags>( |
| AuthenticatorData::Flag::ATTESTATION); |
| std::unique_ptr<AuthenticatorData> authenticator_data = |
| AuthenticatorData::Create( |
| GetTestClientData(authenticator_utils::kCreateType) |
| ->SerializeToJson(), |
| flags, std::vector<uint8_t>(4, 0) /* counter */, |
| std::move(attested_data)); |
| |
| // Construct the attestation statement. |
| std::unique_ptr<FidoAttestationStatement> fido_attestation_statement = |
| FidoAttestationStatement::CreateFromU2fRegisterResponse( |
| GetTestRegisterResponse()); |
| |
| // Construct the attestation object. |
| auto attestation_object = std::make_unique<AttestationObject>( |
| std::move(authenticator_data), std::move(fido_attestation_statement)); |
| |
| EXPECT_EQ(GetTestAttestationObjectBytes(), |
| attestation_object->SerializeToCBOREncodedBytes()); |
| } |
| |
| // Test that a U2F register response is properly parsed. |
| TEST_F(AuthenticatorImplTest, TestRegisterResponseData) { |
| std::unique_ptr<CollectedClientData> client_data = |
| GetTestClientData(authenticator_utils::kCreateType); |
| std::unique_ptr<RegisterResponseData> response = |
| RegisterResponseData::CreateFromU2fRegisterResponse( |
| std::move(client_data), GetTestRegisterResponse()); |
| |
| EXPECT_EQ(std::vector<uint8_t>( |
| kTestClientDataJsonString, |
| kTestClientDataJsonString + strlen(kTestClientDataJsonString)), |
| response->GetClientDataJSONBytes()); |
| EXPECT_EQ(GetTestCredentialRawIdBytes(), response->raw_id()); |
| EXPECT_EQ(GetTestAttestationObjectBytes(), |
| response->GetCBOREncodedAttestationObject()); |
| } |
| |
| } // namespace content |