1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "device/fido/virtual_fido_device.h"
6 
7 #include <algorithm>
8 #include <tuple>
9 #include <utility>
10 
11 #include "base/rand_util.h"
12 #include "crypto/ec_signature_creator.h"
13 #include "device/fido/fido_parsing_utils.h"
14 #include "third_party/boringssl/src/include/openssl/ec_key.h"
15 
16 namespace device {
17 
18 namespace {
19 
20 // The example attestation private key from the U2F spec at
21 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#registration-example
22 //
23 // PKCS.8 encoded without encryption.
24 constexpr uint8_t kAttestationKey[]{
25     0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
26     0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
27     0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
28     0xf3, 0xfc, 0xcc, 0x0d, 0x00, 0xd8, 0x03, 0x19, 0x54, 0xf9, 0x08, 0x64,
29     0xd4, 0x3c, 0x24, 0x7f, 0x4b, 0xf5, 0xf0, 0x66, 0x5c, 0x6b, 0x50, 0xcc,
30     0x17, 0x74, 0x9a, 0x27, 0xd1, 0xcf, 0x76, 0x64, 0xa1, 0x44, 0x03, 0x42,
31     0x00, 0x04, 0x8d, 0x61, 0x7e, 0x65, 0xc9, 0x50, 0x8e, 0x64, 0xbc, 0xc5,
32     0x67, 0x3a, 0xc8, 0x2a, 0x67, 0x99, 0xda, 0x3c, 0x14, 0x46, 0x68, 0x2c,
33     0x25, 0x8c, 0x46, 0x3f, 0xff, 0xdf, 0x58, 0xdf, 0xd2, 0xfa, 0x3e, 0x6c,
34     0x37, 0x8b, 0x53, 0xd7, 0x95, 0xc4, 0xa4, 0xdf, 0xfb, 0x41, 0x99, 0xed,
35     0xd7, 0x86, 0x2f, 0x23, 0xab, 0xaf, 0x02, 0x03, 0xb4, 0xb8, 0x91, 0x1b,
36     0xa0, 0x56, 0x99, 0x94, 0xe1, 0x01};
37 
38 }  // namespace
39 
40 // VirtualFidoDevice::RegistrationData ----------------------------------------
41 
42 VirtualFidoDevice::RegistrationData::RegistrationData() = default;
RegistrationData(std::unique_ptr<crypto::ECPrivateKey> private_key,base::span<const uint8_t,kRpIdHashLength> application_parameter,uint32_t counter)43 VirtualFidoDevice::RegistrationData::RegistrationData(
44     std::unique_ptr<crypto::ECPrivateKey> private_key,
45     base::span<const uint8_t, kRpIdHashLength> application_parameter,
46     uint32_t counter)
47     : private_key(std::move(private_key)),
48       application_parameter(
49           fido_parsing_utils::Materialize(application_parameter)),
50       counter(counter) {}
51 VirtualFidoDevice::RegistrationData::RegistrationData(RegistrationData&& data) =
52     default;
53 VirtualFidoDevice::RegistrationData::~RegistrationData() = default;
54 
55 VirtualFidoDevice::RegistrationData& VirtualFidoDevice::RegistrationData::
56 operator=(RegistrationData&& other) = default;
57 
58 // VirtualFidoDevice::State ---------------------------------------------------
59 
State()60 VirtualFidoDevice::State::State()
61     : attestation_cert_common_name("Batch Certificate"),
62       individual_attestation_cert_common_name("Individual Certificate") {}
63 VirtualFidoDevice::State::~State() = default;
64 
InjectRegistration(base::span<const uint8_t> credential_id,const std::string & relying_party_id)65 bool VirtualFidoDevice::State::InjectRegistration(
66     base::span<const uint8_t> credential_id,
67     const std::string& relying_party_id) {
68   auto application_parameter =
69       fido_parsing_utils::CreateSHA256Hash(relying_party_id);
70   auto private_key = crypto::ECPrivateKey::Create();
71   if (!private_key)
72     return false;
73 
74   RegistrationData registration(std::move(private_key),
75                                 std::move(application_parameter),
76                                 0 /* signature counter */);
77 
78   bool was_inserted;
79   std::tie(std::ignore, was_inserted) = registrations.emplace(
80       fido_parsing_utils::Materialize(credential_id), std::move(registration));
81   return was_inserted;
82 }
83 
InjectResidentKey(base::span<const uint8_t> credential_id,device::PublicKeyCredentialRpEntity rp,device::PublicKeyCredentialUserEntity user,int32_t signature_counter,std::unique_ptr<crypto::ECPrivateKey> private_key)84 bool VirtualFidoDevice::State::InjectResidentKey(
85     base::span<const uint8_t> credential_id,
86     device::PublicKeyCredentialRpEntity rp,
87     device::PublicKeyCredentialUserEntity user,
88     int32_t signature_counter,
89     std::unique_ptr<crypto::ECPrivateKey> private_key) {
90   auto application_parameter = fido_parsing_utils::CreateSHA256Hash(rp.id);
91 
92   // Cannot create a duplicate credential for the same (RP ID, user ID) pair.
93   for (const auto& registration : registrations) {
94     if (registration.second.is_resident &&
95         application_parameter == registration.second.application_parameter &&
96         user.id == registration.second.user->id) {
97       return false;
98     }
99   }
100 
101   RegistrationData registration(std::move(private_key),
102                                 std::move(application_parameter),
103                                 signature_counter);
104   registration.is_resident = true;
105   registration.rp = std::move(rp);
106   registration.user = std::move(user);
107 
108   bool was_inserted;
109   std::tie(std::ignore, was_inserted) = registrations.emplace(
110       fido_parsing_utils::Materialize(credential_id), std::move(registration));
111   return was_inserted;
112 }
113 
InjectResidentKey(base::span<const uint8_t> credential_id,device::PublicKeyCredentialRpEntity rp,device::PublicKeyCredentialUserEntity user)114 bool VirtualFidoDevice::State::InjectResidentKey(
115     base::span<const uint8_t> credential_id,
116     device::PublicKeyCredentialRpEntity rp,
117     device::PublicKeyCredentialUserEntity user) {
118   auto private_key = crypto::ECPrivateKey::Create();
119   DCHECK(private_key);
120   return InjectResidentKey(std::move(credential_id), std::move(rp),
121                            std::move(user), /*signature_counter=*/0,
122                            std::move(private_key));
123 }
124 
InjectResidentKey(base::span<const uint8_t> credential_id,const std::string & relying_party_id,base::span<const uint8_t> user_id,base::Optional<std::string> user_name,base::Optional<std::string> user_display_name)125 bool VirtualFidoDevice::State::InjectResidentKey(
126     base::span<const uint8_t> credential_id,
127     const std::string& relying_party_id,
128     base::span<const uint8_t> user_id,
129     base::Optional<std::string> user_name,
130     base::Optional<std::string> user_display_name) {
131   return InjectResidentKey(
132       credential_id, PublicKeyCredentialRpEntity(std::move(relying_party_id)),
133       PublicKeyCredentialUserEntity(fido_parsing_utils::Materialize(user_id),
134                                     std::move(user_name),
135                                     std::move(user_display_name),
136                                     /*icon_url=*/base::nullopt));
137 }
138 
139 // VirtualFidoDevice ----------------------------------------------------------
140 
141 VirtualFidoDevice::VirtualFidoDevice() = default;
142 
143 
VirtualFidoDevice(scoped_refptr<State> state)144 VirtualFidoDevice::VirtualFidoDevice(scoped_refptr<State> state)
145     : state_(std::move(state)) {}
146 
147 VirtualFidoDevice::~VirtualFidoDevice() = default;
148 
149 // static
GetAttestationKey()150 std::vector<uint8_t> VirtualFidoDevice::GetAttestationKey() {
151   return fido_parsing_utils::Materialize(kAttestationKey);
152 }
153 
154 // static
Sign(crypto::ECPrivateKey * private_key,base::span<const uint8_t> sign_buffer,std::vector<uint8_t> * signature)155 bool VirtualFidoDevice::Sign(crypto::ECPrivateKey* private_key,
156                              base::span<const uint8_t> sign_buffer,
157                              std::vector<uint8_t>* signature) {
158   auto signer = crypto::ECSignatureCreator::Create(private_key);
159   return signer->Sign(sign_buffer.data(), sign_buffer.size(), signature);
160 }
161 
162 base::Optional<std::vector<uint8_t>>
GenerateAttestationCertificate(bool individual_attestation_requested) const163 VirtualFidoDevice::GenerateAttestationCertificate(
164     bool individual_attestation_requested) const {
165   std::unique_ptr<crypto::ECPrivateKey> attestation_private_key =
166       crypto::ECPrivateKey::CreateFromPrivateKeyInfo(GetAttestationKey());
167   constexpr uint32_t kAttestationCertSerialNumber = 1;
168 
169   // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-authenticator-transports-extension-v1.2-ps-20170411.html#fido-u2f-certificate-transports-extension
170   static constexpr uint8_t kTransportTypesOID[] = {
171       0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xe5, 0x1c, 0x02, 0x01, 0x01};
172   uint8_t transport_bit;
173   switch (DeviceTransport()) {
174     case FidoTransportProtocol::kBluetoothLowEnergy:
175     case FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy:
176       transport_bit = 1;
177       break;
178     case FidoTransportProtocol::kUsbHumanInterfaceDevice:
179       transport_bit = 2;
180       break;
181     case FidoTransportProtocol::kNearFieldCommunication:
182       transport_bit = 3;
183       break;
184     case FidoTransportProtocol::kInternal:
185       transport_bit = 4;
186       break;
187   }
188   const uint8_t kTransportTypesContents[] = {
189       3,                            // BIT STRING
190       2,                            // two bytes long
191       8 - transport_bit - 1,        // trailing bits unused
192       0b10000000 >> transport_bit,  // transport
193   };
194   const std::vector<net::x509_util::Extension> extensions = {
195       {kTransportTypesOID, false /* not critical */, kTransportTypesContents},
196   };
197 
198   // https://w3c.github.io/webauthn/#sctn-packed-attestation-cert-requirements
199   std::string attestation_cert;
200   if (!net::x509_util::CreateSelfSignedCert(
201           attestation_private_key->key(), net::x509_util::DIGEST_SHA256,
202           "C=US, O=Chromium, OU=Authenticator Attestation, CN=" +
203               (individual_attestation_requested
204                    ? state_->individual_attestation_cert_common_name
205                    : state_->attestation_cert_common_name),
206           kAttestationCertSerialNumber, base::Time::FromTimeT(1500000000),
207           base::Time::FromTimeT(1500000000), extensions, &attestation_cert)) {
208     DVLOG(2) << "Failed to create attestation certificate";
209     return base::nullopt;
210   }
211 
212   return std::vector<uint8_t>(attestation_cert.begin(), attestation_cert.end());
213 }
214 
StoreNewKey(base::span<const uint8_t> key_handle,VirtualFidoDevice::RegistrationData registration_data)215 void VirtualFidoDevice::StoreNewKey(
216     base::span<const uint8_t> key_handle,
217     VirtualFidoDevice::RegistrationData registration_data) {
218   // Skip storing the registration if this is a dummy request. This prevents
219   // dummy credentials to be returned by the GetCredentials method of the
220   // virtual authenticator API.
221   if (registration_data.application_parameter == device::kBogusAppParam ||
222       registration_data.application_parameter ==
223           fido_parsing_utils::CreateSHA256Hash(kDummyRpID)) {
224     return;
225   }
226 
227   // Store the registration. Because the key handle is the hashed public key we
228   // just generated, no way this should already be registered.
229   bool did_insert = false;
230   std::tie(std::ignore, did_insert) = mutable_state()->registrations.emplace(
231       fido_parsing_utils::Materialize(key_handle),
232       std::move(registration_data));
233   DCHECK(did_insert);
234 }
235 
FindRegistrationData(base::span<const uint8_t> key_handle,base::span<const uint8_t,kRpIdHashLength> application_parameter)236 VirtualFidoDevice::RegistrationData* VirtualFidoDevice::FindRegistrationData(
237     base::span<const uint8_t> key_handle,
238     base::span<const uint8_t, kRpIdHashLength> application_parameter) {
239   // Check if this is our key_handle and it's for this appId.
240   auto it = mutable_state()->registrations.find(key_handle);
241   if (it == mutable_state()->registrations.end())
242     return nullptr;
243 
244   if (!std::equal(application_parameter.begin(), application_parameter.end(),
245                   it->second.application_parameter.begin(),
246                   it->second.application_parameter.end())) {
247     return nullptr;
248   }
249 
250   return &it->second;
251 }
252 
SimulatePress()253 bool VirtualFidoDevice::SimulatePress() {
254   if (!state_->simulate_press_callback)
255     return true;
256 
257   auto weak_this = GetWeakPtr();
258   bool result = state_->simulate_press_callback.Run(this);
259   // |this| might have been destroyed at this point - accessing state from the
260   // object without checking weak_this is dangerous.
261   return weak_this && result;
262 }
263 
TryWink(base::OnceClosure cb)264 void VirtualFidoDevice::TryWink(base::OnceClosure cb) {
265   std::move(cb).Run();
266 }
267 
GetId() const268 std::string VirtualFidoDevice::GetId() const {
269   return id_;
270 }
271 
DeviceTransport() const272 FidoTransportProtocol VirtualFidoDevice::DeviceTransport() const {
273   return state_->transport;
274 }
275 
276 // static
MakeVirtualFidoDeviceId()277 std::string VirtualFidoDevice::MakeVirtualFidoDeviceId() {
278   return "VirtualFidoDevice-" + base::RandBytesAsString(32);
279 }
280 
281 }  // namespace device
282