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