| // 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 "device/u2f/u2f_register.h" |
| |
| #include <iterator> |
| #include <utility> |
| |
| #include "base/run_loop.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "components/cbor/cbor_writer.h" |
| #include "device/u2f/attestation_object.h" |
| #include "device/u2f/attested_credential_data.h" |
| #include "device/u2f/authenticator_data.h" |
| #include "device/u2f/ec_public_key.h" |
| #include "device/u2f/fido_attestation_statement.h" |
| #include "device/u2f/mock_u2f_device.h" |
| #include "device/u2f/mock_u2f_discovery.h" |
| #include "device/u2f/register_response_data.h" |
| #include "device/u2f/u2f_parsing_utils.h" |
| #include "device/u2f/u2f_response_test_data.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace device { |
| |
| using ::testing::_; |
| |
| namespace { |
| |
| constexpr char kTestRelyingPartyId[] = "google.com"; |
| constexpr bool kNoIndividualAttestation = false; |
| |
| // EC public key encoded in COSE_Key format. |
| // x : F868CE3869605224CE1059C0047EF01B830F2AD93BE27A3211F44E894560E695 |
| // y : 4E11538CABA2DF1CC1A6F250ED9F0C8B28B39DA44539DFABD46B589CD0E202E5 |
| constexpr uint8_t kTestECPublicKeyCOSE[] = { |
| // clang-format off |
| 0xA5, // map(5) |
| 0x01, 0x02, // kty: EC key type |
| 0x03, 0x26, // alg: EC256 signature algorithm |
| 0x20, 0x01, // crv: P-256 curve |
| 0x21, // x-coordinate |
| 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, |
| 0x22, // y-coordinate |
| 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, |
| // clang-format on |
| }; |
| |
| // The attested credential data, excluding the public key bytes. Append |
| // with kTestECPublicKeyCOSE 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, 0xC4 // bytes(196). i.e.,the authenticator_data bytearray |
| // clang-format on |
| }; |
| |
| // Helpers for testing U2f register responses. |
| std::vector<uint8_t> GetTestECPublicKeyCOSE() { |
| return std::vector<uint8_t>(std::begin(kTestECPublicKeyCOSE), |
| std::end(kTestECPublicKeyCOSE)); |
| } |
| |
| std::vector<uint8_t> GetTestRegisterResponse() { |
| return std::vector<uint8_t>(std::begin(test_data::kTestU2fRegisterResponse), |
| std::end(test_data::kTestU2fRegisterResponse)); |
| } |
| |
| std::vector<uint8_t> GetTestCredentialRawIdBytes() { |
| return std::vector<uint8_t>(std::begin(test_data::kTestCredentialRawIdBytes), |
| std::end(test_data::kTestCredentialRawIdBytes)); |
| } |
| |
| std::vector<uint8_t> GetU2fAttestationStatementCBOR() { |
| return std::vector<uint8_t>(std::begin(kU2fAttestationStatementCBOR), |
| std::end(kU2fAttestationStatementCBOR)); |
| } |
| |
| std::vector<uint8_t> GetTestAttestedCredentialDataBytes() { |
| // Combine kTestAttestedCredentialDataPrefix and kTestECPublicKeyCOSE. |
| std::vector<uint8_t> test_attested_data( |
| std::begin(kTestAttestedCredentialDataPrefix), |
| std::end(kTestAttestedCredentialDataPrefix)); |
| test_attested_data.insert(test_attested_data.end(), |
| std::begin(kTestECPublicKeyCOSE), |
| std::end(kTestECPublicKeyCOSE)); |
| 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; |
| } |
| |
| // Convenience functions for setting a mock discovery. |
| MockU2fDiscovery* SetMockDiscovery( |
| U2fRequest* request, |
| std::unique_ptr<MockU2fDiscovery> discovery) { |
| auto* raw_discovery = discovery.get(); |
| std::vector<std::unique_ptr<U2fDiscovery>> discoveries; |
| discoveries.push_back(std::move(discovery)); |
| request->SetDiscoveriesForTesting(std::move(discoveries)); |
| return raw_discovery; |
| } |
| |
| } // namespace |
| |
| class U2fRegisterTest : public testing::Test { |
| public: |
| U2fRegisterTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::UI) {} |
| |
| protected: |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| }; |
| |
| class TestRegisterCallback { |
| public: |
| TestRegisterCallback() |
| : callback_(base::BindOnce(&TestRegisterCallback::ReceivedCallback, |
| base::Unretained(this))) {} |
| ~TestRegisterCallback() = default; |
| |
| void ReceivedCallback(U2fReturnCode status_code, |
| base::Optional<RegisterResponseData> response_data) { |
| response_ = std::make_pair(status_code, std::move(response_data)); |
| closure_.Run(); |
| } |
| |
| const std::pair<U2fReturnCode, base::Optional<RegisterResponseData>>& |
| WaitForCallback() { |
| closure_ = run_loop_.QuitClosure(); |
| run_loop_.Run(); |
| return response_; |
| } |
| |
| U2fRegister::RegisterResponseCallback callback() { |
| return std::move(callback_); |
| } |
| |
| private: |
| std::pair<U2fReturnCode, base::Optional<RegisterResponseData>> response_; |
| base::Closure closure_; |
| U2fRegister::RegisterResponseCallback callback_; |
| base::RunLoop run_loop_; |
| }; |
| |
| TEST_F(U2fRegisterTest, TestRegisterSuccess) { |
| base::flat_set<U2fTransportProtocol> protocols; |
| std::vector<std::vector<uint8_t>> registration_keys; |
| TestRegisterCallback cb; |
| |
| auto request = std::make_unique<U2fRegister>( |
| kTestRelyingPartyId, nullptr, protocols, registration_keys, |
| std::vector<uint8_t>(32), std::vector<uint8_t>(32), |
| kNoIndividualAttestation, cb.callback()); |
| |
| auto* discovery = |
| SetMockDiscovery(request.get(), std::make_unique<MockU2fDiscovery>()); |
| EXPECT_CALL(*discovery, Start()) |
| .WillOnce( |
| testing::Invoke(discovery, &MockU2fDiscovery::StartSuccessAsync)); |
| request->Start(); |
| |
| auto device = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device")); |
| EXPECT_CALL(*device, DeviceTransactPtr(_, _)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); |
| EXPECT_CALL(*device, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device)); |
| |
| const std::pair<U2fReturnCode, base::Optional<RegisterResponseData>>& |
| response = cb.WaitForCallback(); |
| EXPECT_EQ(U2fReturnCode::SUCCESS, std::get<0>(response)); |
| EXPECT_EQ(GetTestCredentialRawIdBytes(), std::get<1>(response)->raw_id()); |
| } |
| |
| TEST_F(U2fRegisterTest, TestDelayedSuccess) { |
| base::flat_set<U2fTransportProtocol> protocols; |
| std::vector<std::vector<uint8_t>> registration_keys; |
| TestRegisterCallback cb; |
| |
| auto request = std::make_unique<U2fRegister>( |
| kTestRelyingPartyId, nullptr, protocols, registration_keys, |
| std::vector<uint8_t>(32), std::vector<uint8_t>(32), |
| kNoIndividualAttestation, cb.callback()); |
| |
| auto* discovery = |
| SetMockDiscovery(request.get(), std::make_unique<MockU2fDiscovery>()); |
| EXPECT_CALL(*discovery, Start()) |
| .WillOnce( |
| testing::Invoke(discovery, &MockU2fDiscovery::StartSuccessAsync)); |
| request->Start(); |
| |
| auto device = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device")); |
| // Go through the state machine twice before success. |
| EXPECT_CALL(*device, DeviceTransactPtr(_, _)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); |
| EXPECT_CALL(*device, TryWinkRef(_)) |
| .Times(2) |
| .WillRepeatedly(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device)); |
| |
| const std::pair<U2fReturnCode, base::Optional<RegisterResponseData>>& |
| response = cb.WaitForCallback(); |
| EXPECT_EQ(U2fReturnCode::SUCCESS, std::get<0>(response)); |
| EXPECT_EQ(GetTestCredentialRawIdBytes(), std::get<1>(response)->raw_id()); |
| } |
| |
| TEST_F(U2fRegisterTest, TestMultipleDevices) { |
| base::flat_set<U2fTransportProtocol> protocols; |
| std::vector<std::vector<uint8_t>> registration_keys; |
| TestRegisterCallback cb; |
| |
| auto request = std::make_unique<U2fRegister>( |
| kTestRelyingPartyId, nullptr, protocols, registration_keys, |
| std::vector<uint8_t>(32), std::vector<uint8_t>(32), |
| kNoIndividualAttestation, cb.callback()); |
| |
| auto* discovery = |
| SetMockDiscovery(request.get(), std::make_unique<MockU2fDiscovery>()); |
| EXPECT_CALL(*discovery, Start()) |
| .WillOnce( |
| testing::Invoke(discovery, &MockU2fDiscovery::StartSuccessAsync)); |
| request->Start(); |
| |
| auto device0 = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0")); |
| EXPECT_CALL(*device0, DeviceTransactPtr(_, _)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied)); |
| // One wink per device. |
| EXPECT_CALL(*device0, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device0)); |
| |
| // Second device will have a successful touch. |
| auto device1 = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1")); |
| EXPECT_CALL(*device1, DeviceTransactPtr(_, _)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); |
| // One wink per device. |
| EXPECT_CALL(*device1, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device1)); |
| |
| const std::pair<U2fReturnCode, base::Optional<RegisterResponseData>>& |
| response = cb.WaitForCallback(); |
| EXPECT_EQ(U2fReturnCode::SUCCESS, std::get<0>(response)); |
| EXPECT_EQ(GetTestCredentialRawIdBytes(), |
| std::get<1>(response).value().raw_id()); |
| } |
| |
| // Tests a scenario where a single device is connected and registration call |
| // is received with three unknown key handles. We expect that three check only |
| // sign-in calls be processed before registration. |
| TEST_F(U2fRegisterTest, TestSingleDeviceRegistrationWithExclusionList) { |
| base::flat_set<U2fTransportProtocol> protocols; |
| // Simulate three unknown key handles. |
| std::vector<std::vector<uint8_t>> handles; |
| handles.emplace_back(32, 0xB); |
| handles.emplace_back(32, 0xC); |
| handles.emplace_back(32, 0xD); |
| TestRegisterCallback cb; |
| |
| auto request = std::make_unique<U2fRegister>( |
| kTestRelyingPartyId, nullptr, protocols, handles, |
| std::vector<uint8_t>(32), std::vector<uint8_t>(32), |
| kNoIndividualAttestation, cb.callback()); |
| |
| auto* discovery = |
| SetMockDiscovery(request.get(), std::make_unique<MockU2fDiscovery>()); |
| EXPECT_CALL(*discovery, Start()) |
| .WillOnce( |
| testing::Invoke(discovery, &MockU2fDiscovery::StartSuccessAsync)); |
| request->Start(); |
| |
| auto device = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device")); |
| // DeviceTransact() will be called four times including three check |
| // only sign-in calls and one registration call. For the first three calls, |
| // device will invoke MockU2fDevice::WrongData as the authenticator did not |
| // create the three key handles provided in the exclude list. At the fourth |
| // call, MockU2fDevice::NoErrorRegister will be invoked after registration. |
| EXPECT_CALL(*device.get(), DeviceTransactPtr(_, _)) |
| .Times(4) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); |
| // TryWink() will be called twice. First during the check only sign-in. After |
| // check only sign operation is complete, request state is changed to IDLE, |
| // and TryWink() is called again before Register() is called. |
| EXPECT_CALL(*device, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device)); |
| |
| const std::pair<U2fReturnCode, base::Optional<RegisterResponseData>>& |
| response = cb.WaitForCallback(); |
| EXPECT_EQ(U2fReturnCode::SUCCESS, std::get<0>(response)); |
| EXPECT_EQ(GetTestCredentialRawIdBytes(), std::get<1>(response)->raw_id()); |
| } |
| |
| // Tests a scenario where two devices are connected and registration call is |
| // received with three unknown key handles. We assume that user will proceed the |
| // registration with second device, "device1". |
| TEST_F(U2fRegisterTest, TestMultipleDeviceRegistrationWithExclusionList) { |
| base::flat_set<U2fTransportProtocol> protocols; |
| // Simulate three unknown key handles. |
| std::vector<std::vector<uint8_t>> handles; |
| handles.emplace_back(32, 0xB); |
| handles.emplace_back(32, 0xC); |
| handles.emplace_back(32, 0xD); |
| TestRegisterCallback cb; |
| |
| auto request = std::make_unique<U2fRegister>( |
| kTestRelyingPartyId, nullptr, protocols, handles, |
| std::vector<uint8_t>(32), std::vector<uint8_t>(32), |
| kNoIndividualAttestation, cb.callback()); |
| |
| auto* discovery = |
| SetMockDiscovery(request.get(), std::make_unique<MockU2fDiscovery>()); |
| EXPECT_CALL(*discovery, Start()) |
| .WillOnce( |
| testing::Invoke(discovery, &MockU2fDiscovery::StartSuccessAsync)); |
| request->Start(); |
| |
| auto device0 = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0")); |
| // DeviceTransact() will be called four times: three times to check for |
| // duplicate key handles and once for registration. Since user |
| // will register using "device1", the fourth call will invoke |
| // MockU2fDevice::NotSatisfied. |
| EXPECT_CALL(*device0, DeviceTransactPtr(_, _)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied)); |
| // TryWink() will be called twice on both devices -- during check only |
| // sign-in operation and during registration attempt. |
| EXPECT_CALL(*device0, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device0)); |
| |
| auto device1 = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1")); |
| // We assume that user registers with second device. Therefore, the fourth |
| // DeviceTransact() will invoke MockU2fDevice::NoErrorRegister after |
| // successful registration. |
| EXPECT_CALL(*device1, DeviceTransactPtr(_, _)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); |
| // TryWink() will be called twice on both devices -- during check only |
| // sign-in operation and during registration attempt. |
| EXPECT_CALL(*device1, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device1)); |
| |
| const std::pair<U2fReturnCode, base::Optional<RegisterResponseData>>& |
| response = cb.WaitForCallback(); |
| EXPECT_EQ(U2fReturnCode::SUCCESS, std::get<0>(response)); |
| EXPECT_EQ(GetTestCredentialRawIdBytes(), std::get<1>(response)->raw_id()); |
| } |
| |
| // Tests a scenario where single device is connected and registration is called |
| // with a key in the exclude list that was created by this device. We assume |
| // that the duplicate key is the last key handle in the exclude list. Therefore, |
| // after duplicate key handle is found, the process is expected to terminate |
| // after calling bogus registration which checks for user presence. |
| TEST_F(U2fRegisterTest, TestSingleDeviceRegistrationWithDuplicateHandle) { |
| base::flat_set<U2fTransportProtocol> protocols; |
| // Simulate three unknown key handles followed by a duplicate key. |
| std::vector<std::vector<uint8_t>> handles; |
| handles.emplace_back(32, 0xB); |
| handles.emplace_back(32, 0xC); |
| handles.emplace_back(32, 0xD); |
| handles.emplace_back(32, 0xA); |
| TestRegisterCallback cb; |
| |
| auto request = std::make_unique<U2fRegister>( |
| kTestRelyingPartyId, nullptr, protocols, handles, |
| std::vector<uint8_t>(32), std::vector<uint8_t>(32), |
| kNoIndividualAttestation, cb.callback()); |
| |
| auto* discovery = |
| SetMockDiscovery(request.get(), std::make_unique<MockU2fDiscovery>()); |
| EXPECT_CALL(*discovery, Start()) |
| .WillOnce( |
| testing::Invoke(discovery, &MockU2fDiscovery::StartSuccessAsync)); |
| request->Start(); |
| |
| auto device = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device")); |
| // For four keys in exclude list, the first three keys will invoke |
| // MockU2fDevice::WrongData and the final duplicate key handle will invoke |
| // MockU2fDevice::NoErrorSign. Once duplicate key handle is found, bogus |
| // registration is called to confirm user presence. This invokes |
| // MockU2fDevice::NoErrorRegister. |
| EXPECT_CALL(*device, DeviceTransactPtr(_, _)) |
| .Times(5) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); |
| // Since duplicate key handle is found, registration process is terminated |
| // before actual Register() is called on the device. Therefore, TryWink() is |
| // invoked once. |
| EXPECT_CALL(*device, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device)); |
| |
| const std::pair<U2fReturnCode, base::Optional<RegisterResponseData>>& |
| response = cb.WaitForCallback(); |
| EXPECT_EQ(U2fReturnCode::CONDITIONS_NOT_SATISFIED, std::get<0>(response)); |
| EXPECT_EQ(base::nullopt, std::get<1>(response)); |
| } |
| |
| // Tests a scenario where one (device1) of the two devices connected has created |
| // a key handle provided in exclude list. We assume that duplicate key is the |
| // fourth key handle provided in the exclude list. |
| TEST_F(U2fRegisterTest, TestMultipleDeviceRegistrationWithDuplicateHandle) { |
| base::flat_set<U2fTransportProtocol> protocols; |
| // Simulate three unknown key handles followed by a duplicate key. |
| std::vector<std::vector<uint8_t>> handles; |
| handles.emplace_back(32, 0xB); |
| handles.emplace_back(32, 0xC); |
| handles.emplace_back(32, 0xD); |
| handles.emplace_back(32, 0xA); |
| TestRegisterCallback cb; |
| |
| auto request = std::make_unique<U2fRegister>( |
| kTestRelyingPartyId, nullptr, protocols, handles, |
| std::vector<uint8_t>(32), std::vector<uint8_t>(32), |
| kNoIndividualAttestation, cb.callback()); |
| |
| auto* discovery = |
| SetMockDiscovery(request.get(), std::make_unique<MockU2fDiscovery>()); |
| EXPECT_CALL(*discovery, Start()) |
| .WillOnce( |
| testing::Invoke(discovery, &MockU2fDiscovery::StartSuccessAsync)); |
| request->Start(); |
| |
| auto device0 = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0")); |
| // Since the first device did not create any of the key handles provided in |
| // exclude list, we expect that check only sign() should be called |
| // four times, and all the calls to DeviceTransact() invoke |
| // MockU2fDevice::WrongData. |
| EXPECT_CALL(*device0, DeviceTransactPtr(_, _)) |
| .Times(4) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)); |
| EXPECT_CALL(*device0, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device0)); |
| |
| auto device1 = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1")); |
| // Since the last key handle in exclude list is a duplicate key, we expect |
| // that the first three calls to check only sign() invoke |
| // MockU2fDevice::WrongData and that fourth sign() call invoke |
| // MockU2fDevice::NoErrorSign. After duplicate key is found, process is |
| // terminated after user presence is verified using bogus registration, which |
| // invokes MockU2fDevice::NoErrorRegister. |
| EXPECT_CALL(*device1, DeviceTransactPtr(_, _)) |
| .Times(5) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); |
| EXPECT_CALL(*device1, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device1)); |
| |
| const std::pair<U2fReturnCode, base::Optional<RegisterResponseData>>& |
| response = cb.WaitForCallback(); |
| EXPECT_EQ(U2fReturnCode::CONDITIONS_NOT_SATISFIED, std::get<0>(response)); |
| EXPECT_EQ(base::nullopt, std::get<1>(response)); |
| } |
| |
| // These test the parsing of the U2F raw bytes of the registration response. |
| // Test that an EC public key serializes to CBOR properly. |
| TEST_F(U2fRegisterTest, TestSerializedPublicKey) { |
| std::unique_ptr<ECPublicKey> public_key = |
| ECPublicKey::ExtractFromU2fRegistrationResponse( |
| u2f_parsing_utils::kEs256, GetTestRegisterResponse()); |
| EXPECT_EQ(GetTestECPublicKeyCOSE(), public_key->EncodeAsCOSEKey()); |
| } |
| |
| // Test that the attestation statement cbor map is constructed properly. |
| TEST_F(U2fRegisterTest, TestU2fAttestationStatementCBOR) { |
| std::unique_ptr<FidoAttestationStatement> fido_attestation_statement = |
| FidoAttestationStatement::CreateFromU2fRegisterResponse( |
| GetTestRegisterResponse()); |
| auto cbor = cbor::CBORWriter::Write( |
| cbor::CBORValue(fido_attestation_statement->GetAsCBORMap())); |
| ASSERT_TRUE(cbor); |
| EXPECT_EQ(GetU2fAttestationStatementCBOR(), *cbor); |
| } |
| |
| // Tests that well-formed attested credential data serializes properly. |
| TEST_F(U2fRegisterTest, TestAttestedCredentialData) { |
| std::unique_ptr<ECPublicKey> public_key = |
| ECPublicKey::ExtractFromU2fRegistrationResponse( |
| u2f_parsing_utils::kEs256, GetTestRegisterResponse()); |
| base::Optional<AttestedCredentialData> attested_data = |
| AttestedCredentialData::CreateFromU2fRegisterResponse( |
| GetTestRegisterResponse(), std::vector<uint8_t>(16) /* aaguid */, |
| std::move(public_key)); |
| |
| EXPECT_EQ(GetTestAttestedCredentialDataBytes(), |
| attested_data->SerializeAsBytes()); |
| } |
| |
| // Tests that well-formed authenticator data serializes properly. |
| TEST_F(U2fRegisterTest, TestAuthenticatorData) { |
| std::unique_ptr<ECPublicKey> public_key = |
| ECPublicKey::ExtractFromU2fRegistrationResponse( |
| u2f_parsing_utils::kEs256, GetTestRegisterResponse()); |
| base::Optional<AttestedCredentialData> attested_data = |
| AttestedCredentialData::CreateFromU2fRegisterResponse( |
| GetTestRegisterResponse(), std::vector<uint8_t>(16) /* aaguid */, |
| std::move(public_key)); |
| |
| constexpr uint8_t flags = |
| static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence) | |
| static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation); |
| |
| AuthenticatorData authenticator_data(kTestRelyingPartyId, flags, |
| std::vector<uint8_t>(4) /* counter */, |
| std::move(attested_data)); |
| |
| EXPECT_EQ(GetTestAuthenticatorDataBytes(), |
| authenticator_data.SerializeToByteArray()); |
| } |
| |
| // Tests that a U2F attestation object serializes properly. |
| TEST_F(U2fRegisterTest, TestU2fAttestationObject) { |
| std::unique_ptr<ECPublicKey> public_key = |
| ECPublicKey::ExtractFromU2fRegistrationResponse( |
| u2f_parsing_utils::kEs256, GetTestRegisterResponse()); |
| base::Optional<AttestedCredentialData> attested_data = |
| AttestedCredentialData::CreateFromU2fRegisterResponse( |
| GetTestRegisterResponse(), std::vector<uint8_t>(16) /* aaguid */, |
| std::move(public_key)); |
| |
| constexpr uint8_t flags = |
| static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence) | |
| static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation); |
| AuthenticatorData authenticator_data(kTestRelyingPartyId, flags, |
| std::vector<uint8_t>(4) /* 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(U2fRegisterTest, TestRegisterResponseData) { |
| base::Optional<RegisterResponseData> response = |
| RegisterResponseData::CreateFromU2fRegisterResponse( |
| kTestRelyingPartyId, GetTestRegisterResponse()); |
| EXPECT_EQ(GetTestCredentialRawIdBytes(), response->raw_id()); |
| EXPECT_EQ(GetTestAttestationObjectBytes(), |
| response->GetCBOREncodedAttestationObject()); |
| } |
| |
| MATCHER_P(IndicatesIndividualAttestation, expected, "") { |
| const std::vector<uint8_t> cmd = arg->GetEncodedCommand(); |
| return cmd.size() >= 2 && ((cmd[2] & 0x80) == 0x80) == expected; |
| } |
| |
| TEST_F(U2fRegisterTest, TestIndividualAttestation) { |
| // Test that the individual attestation flag is correctly reflected in the |
| // resulting registration APDU. |
| for (const auto& individual_attestation : {false, true}) { |
| SCOPED_TRACE(individual_attestation); |
| |
| base::flat_set<U2fTransportProtocol> protocols; |
| std::vector<std::vector<uint8_t>> registration_keys; |
| TestRegisterCallback cb; |
| |
| auto request = std::make_unique<U2fRegister>( |
| kTestRelyingPartyId, nullptr, protocols, registration_keys, |
| std::vector<uint8_t>(32), std::vector<uint8_t>(32), |
| individual_attestation, cb.callback()); |
| |
| auto* discovery = |
| SetMockDiscovery(request.get(), std::make_unique<MockU2fDiscovery>()); |
| EXPECT_CALL(*discovery, Start()) |
| .WillOnce( |
| testing::Invoke(discovery, &MockU2fDiscovery::StartSuccessAsync)); |
| request->Start(); |
| |
| auto device = std::make_unique<MockU2fDevice>(); |
| EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device")); |
| EXPECT_CALL(*device, |
| DeviceTransactPtr( |
| IndicatesIndividualAttestation(individual_attestation), _)) |
| .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); |
| EXPECT_CALL(*device, TryWinkRef(_)) |
| .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); |
| discovery->AddDevice(std::move(device)); |
| |
| const std::pair<U2fReturnCode, base::Optional<RegisterResponseData>>& |
| response = cb.WaitForCallback(); |
| EXPECT_EQ(U2fReturnCode::SUCCESS, std::get<0>(response)); |
| EXPECT_EQ(GetTestCredentialRawIdBytes(), std::get<1>(response)->raw_id()); |
| } |
| } |
| |
| } // namespace device |