blob: 01f19b3f349587c3de39c26348841ffb5ca5d9ad [file] [log] [blame]
Maksim Ivanov43477a92018-12-03 04:59:27 +01001// Copyright 2018 The Chromium OS 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 "cryptohome/key_challenge_service_impl.h"
6
7#include <cstdint>
8#include <memory>
9#include <utility>
10#include <vector>
11
12#include <base/bind.h>
Qijiang Fan713061e2021-03-08 15:45:12 +090013#include <base/check.h>
Maksim Ivanov43477a92018-12-03 04:59:27 +010014#include <base/logging.h>
John L Chen5fcf4062019-05-13 02:27:51 +080015#include <base/optional.h>
Maksim Ivanov9de3f8b2020-02-26 18:35:40 +010016#include <base/time/time.h>
Maksim Ivanov43477a92018-12-03 04:59:27 +010017#include <brillo/errors/error.h>
18#include <dbus/bus.h>
19#include <google/protobuf/message_lite.h>
20
21namespace cryptohome {
22
23namespace {
24
Maksim Ivanov9de3f8b2020-02-26 18:35:40 +010025// This is currently equal to the timeout used by the Chrome when making
26// MountEx/CheckKeyEx calls to cryptohomed. (These timeouts are not technically
27// required to be equal, but it's good from the UX perspective).
28constexpr base::TimeDelta kDbusCallTimeout = base::TimeDelta::FromMinutes(2);
29
John L Chen5fcf4062019-05-13 02:27:51 +080030// Used for holding OnceCallback when multiple callback function needs it, but
31// only one of them will run. Note: This is not thread safe.
32template <typename T>
33class OnceCallbackHolder {
34 public:
35 explicit OnceCallbackHolder(T obj) : obj_(std::move(obj)) {}
36
37 T get() {
John L Chenc36f8b02019-05-14 21:52:32 +080038 DCHECK(obj_.has_value());
John L Chen5fcf4062019-05-13 02:27:51 +080039 base::Optional<T> res;
40 std::swap(res, obj_);
John L Chen5fcf4062019-05-13 02:27:51 +080041 return std::move(res.value());
42 }
43
44 private:
45 // The object that we are holding
46 base::Optional<T> obj_;
47};
48
Maksim Ivanov43477a92018-12-03 04:59:27 +010049std::vector<uint8_t> SerializeProto(
50 const google::protobuf::MessageLite& proto) {
51 std::vector<uint8_t> serialized_proto(proto.ByteSizeLong());
52 CHECK(
53 proto.SerializeToArray(serialized_proto.data(), serialized_proto.size()));
54 return serialized_proto;
55}
56
57bool DeserializeProto(const std::vector<uint8_t>& raw_buf,
58 google::protobuf::MessageLite* proto) {
59 return proto->ParseFromArray(raw_buf.data(), raw_buf.size());
60}
61
62void OnDBusChallengeKeySuccess(
John L Chen5fcf4062019-05-13 02:27:51 +080063 std::shared_ptr<OnceCallbackHolder<KeyChallengeService::ResponseCallback>>
64 callback_holder,
Maksim Ivanov43477a92018-12-03 04:59:27 +010065 const std::vector<uint8_t>& challenge_response) {
John L Chen5fcf4062019-05-13 02:27:51 +080066 KeyChallengeService::ResponseCallback original_callback =
67 callback_holder->get();
Maksim Ivanov43477a92018-12-03 04:59:27 +010068 if (challenge_response.empty()) {
Maksim Ivanovdf2e5282020-01-30 02:00:27 +010069 // TODO(crbug.com/1046860): Remove the logging after stabilizing the
70 // feature.
71 LOG(INFO) << "Signature key challenge failed: empty response";
John L Chen5fcf4062019-05-13 02:27:51 +080072 std::move(original_callback).Run(nullptr /* response */);
Maksim Ivanov43477a92018-12-03 04:59:27 +010073 return;
74 }
75 auto response_proto = std::make_unique<KeyChallengeResponse>();
76 if (!DeserializeProto(challenge_response, response_proto.get())) {
77 LOG(ERROR)
78 << "Failed to parse KeyChallengeResponse from ChallengeKey D-Bus call";
John L Chen5fcf4062019-05-13 02:27:51 +080079 std::move(original_callback).Run(nullptr /* response */);
Maksim Ivanov43477a92018-12-03 04:59:27 +010080 return;
81 }
Maksim Ivanovdf2e5282020-01-30 02:00:27 +010082 // TODO(crbug.com/1046860): Remove the logging after stabilizing the feature.
83 if (response_proto->has_signature_response_data()) {
84 LOG(INFO) << "Signature key challenge succeeded: signature size "
85 << response_proto->signature_response_data().signature().size();
86 } else {
87 LOG(INFO) << "Key challenge completed with no signature";
88 }
John L Chen5fcf4062019-05-13 02:27:51 +080089 std::move(original_callback).Run(std::move(response_proto));
Maksim Ivanov43477a92018-12-03 04:59:27 +010090}
91
92void OnDBusChallengeKeyFailure(
John L Chen5fcf4062019-05-13 02:27:51 +080093 std::shared_ptr<OnceCallbackHolder<KeyChallengeService::ResponseCallback>>
94 callback_holder,
Maksim Ivanovdf2e5282020-01-30 02:00:27 +010095 brillo::Error* error) {
96 // TODO(crbug.com/1046860): Remove the logging after stabilizing the feature.
97 if (error) {
98 LOG(INFO) << "Signature key challenge failed: dbus error code "
99 << error->GetCode() << ", message " << error->GetMessage();
100 } else {
101 LOG(INFO) << "Key challenge failed: unknown dbus error";
102 }
John L Chen5fcf4062019-05-13 02:27:51 +0800103 KeyChallengeService::ResponseCallback original_callback =
104 callback_holder->get();
105 std::move(original_callback).Run(nullptr /* response */);
Maksim Ivanov43477a92018-12-03 04:59:27 +0100106}
107
Xiaoyong Zhoud42c2422019-11-15 14:29:30 -0800108void OnDBusFidoMakeCredentialSuccess(
109 std::shared_ptr<OnceCallbackHolder<
110 KeyChallengeService::MakeCredentialCallback>> callback_holder,
111 const std::vector<uint8_t>& make_credential_response) {
112 KeyChallengeService::MakeCredentialCallback original_callback =
113 callback_holder->get();
114
115 if (make_credential_response.empty()) {
116 std::move(original_callback).Run(nullptr /* response */);
117 return;
118 }
119 auto response =
120 std::make_unique<cryptohome::fido::MakeCredentialAuthenticatorResponse>();
121 if (!DeserializeProto(make_credential_response, response.get())) {
122 LOG(ERROR) << "Failed to parse MakeCredentialAuthenticatorResponse from "
123 << "FidoMakeCredential D-Bus call";
124 return;
125 }
126 std::move(original_callback).Run(std::move(response));
127}
128
129void OnDBusFidoMakeCredentialFailure(
130 std::shared_ptr<OnceCallbackHolder<
131 KeyChallengeService::MakeCredentialCallback>> callback_holder,
132 brillo::Error* error) {
133 LOG(ERROR) << error->GetMessage();
134 KeyChallengeService::MakeCredentialCallback original_callback =
135 callback_holder->get();
136 std::move(original_callback).Run(nullptr /* response */);
137}
138
139void OnDBusFidoGetAssertionSuccess(
140 std::shared_ptr<OnceCallbackHolder<
141 KeyChallengeService::GetAssertionCallback>> callback_holder,
142 const std::vector<uint8_t>& get_assertion_response) {
143 KeyChallengeService::GetAssertionCallback original_callback =
144 callback_holder->get();
145
146 if (get_assertion_response.empty()) {
147 std::move(original_callback).Run(nullptr /* response */);
148 return;
149 }
150
151 auto response =
152 std::make_unique<cryptohome::fido::GetAssertionAuthenticatorResponse>();
153 if (!DeserializeProto(get_assertion_response, response.get())) {
154 LOG(ERROR) << "Failed to parse GetAssertionAuthenticatorResponse from "
155 << "FidoGetAssertion D-Bus call";
156 return;
157 }
158 std::move(original_callback).Run(std::move(response));
159}
160
161void OnDBusFidoGetAssertionFailure(
162 std::shared_ptr<OnceCallbackHolder<
163 KeyChallengeService::GetAssertionCallback>> callback_holder,
164 brillo::Error* error) {
165 LOG(ERROR) << error->GetMessage();
166 KeyChallengeService::GetAssertionCallback original_callback =
167 callback_holder->get();
168 std::move(original_callback).Run(nullptr /* response */);
169}
170
Maksim Ivanov43477a92018-12-03 04:59:27 +0100171} // namespace
172
173KeyChallengeServiceImpl::KeyChallengeServiceImpl(
174 scoped_refptr<dbus::Bus> dbus_bus,
175 const std::string& key_delegate_dbus_service_name)
Maksim Ivanove6232ad2019-03-13 21:13:22 +0100176 : key_delegate_dbus_service_name_(key_delegate_dbus_service_name),
177 dbus_proxy_(dbus_bus, key_delegate_dbus_service_name_) {
Maksim Ivanov43477a92018-12-03 04:59:27 +0100178 DCHECK(dbus_bus);
Maksim Ivanove6232ad2019-03-13 21:13:22 +0100179 DCHECK(!key_delegate_dbus_service_name_.empty());
Maksim Ivanov43477a92018-12-03 04:59:27 +0100180}
181
182KeyChallengeServiceImpl::~KeyChallengeServiceImpl() = default;
183
184void KeyChallengeServiceImpl::ChallengeKey(
185 const AccountIdentifier& account_id,
186 const KeyChallengeRequest& key_challenge_request,
John L Chen5fcf4062019-05-13 02:27:51 +0800187 ResponseCallback response_callback) {
Maksim Ivanove6232ad2019-03-13 21:13:22 +0100188 if (!dbus_validate_bus_name(key_delegate_dbus_service_name_.c_str(),
189 nullptr /* error */)) {
190 // Bail out to avoid crashing inside the D-Bus library.
191 // TODO(emaxx): Remove this special handling once libchrome is uprev'ed to
192 // include the fix from crbug.com/927196.
Xiaoyong Zhoud42c2422019-11-15 14:29:30 -0800193 LOG(ERROR) << "Invalid key challenge service name "
194 << key_delegate_dbus_service_name_;
John L Chen5fcf4062019-05-13 02:27:51 +0800195 std::move(response_callback).Run(nullptr /* response */);
Maksim Ivanove6232ad2019-03-13 21:13:22 +0100196 return;
197 }
John L Chen5fcf4062019-05-13 02:27:51 +0800198 std::shared_ptr<OnceCallbackHolder<ResponseCallback>> callback_holder(
199 new OnceCallbackHolder<ResponseCallback>(std::move(response_callback)));
Maksim Ivanovdf2e5282020-01-30 02:00:27 +0100200 // TODO(crbug.com/1046860): Remove the logging after stabilizing the feature.
201 if (key_challenge_request.has_signature_request_data()) {
202 LOG(INFO)
203 << "Starting signature key challenge request, size "
204 << key_challenge_request.signature_request_data().data_to_sign().size()
205 << ", spki size "
206 << key_challenge_request.signature_request_data()
207 .public_key_spki_der()
208 .size()
209 << ", algorithm "
210 << key_challenge_request.signature_request_data().signature_algorithm();
211 }
Maksim Ivanov43477a92018-12-03 04:59:27 +0100212 dbus_proxy_.ChallengeKeyAsync(
213 SerializeProto(account_id), SerializeProto(key_challenge_request),
John L Chen5fcf4062019-05-13 02:27:51 +0800214 base::Bind(&OnDBusChallengeKeySuccess, callback_holder),
Maksim Ivanov9de3f8b2020-02-26 18:35:40 +0100215 base::Bind(&OnDBusChallengeKeyFailure, callback_holder),
216 /*timeout_ms=*/kDbusCallTimeout.InMilliseconds());
Maksim Ivanov43477a92018-12-03 04:59:27 +0100217}
218
Xiaoyong Zhoud42c2422019-11-15 14:29:30 -0800219void KeyChallengeServiceImpl::FidoMakeCredential(
220 const std::string& client_data_json,
221 const cryptohome::fido::PublicKeyCredentialCreationOptions& request,
222 MakeCredentialCallback response_callback) {
223 if (!dbus_validate_bus_name(key_delegate_dbus_service_name_.c_str(),
224 nullptr /* error */)) {
225 LOG(ERROR) << "Invalid key challenge service name "
226 << key_delegate_dbus_service_name_;
227 std::move(response_callback).Run(nullptr /* response */);
228 return;
229 }
230
231 std::shared_ptr<OnceCallbackHolder<MakeCredentialCallback>> callback_holder(
232 new OnceCallbackHolder<MakeCredentialCallback>(
233 std::move(response_callback)));
234 dbus_proxy_.FidoMakeCredentialAsync(
235 client_data_json, SerializeProto(request),
236 base::Bind(&OnDBusFidoMakeCredentialSuccess, callback_holder),
237 base::Bind(&OnDBusFidoMakeCredentialFailure, callback_holder));
238}
239
240void KeyChallengeServiceImpl::FidoGetAssertion(
241 const std::string& client_data_json,
242 const cryptohome::fido::PublicKeyCredentialRequestOptions& request,
243 GetAssertionCallback response_callback) {
244 if (!dbus_validate_bus_name(key_delegate_dbus_service_name_.c_str(),
245 nullptr)) {
246 LOG(ERROR) << "Invalid key challenge service name "
247 << key_delegate_dbus_service_name_;
248 std::move(response_callback).Run(nullptr /* response */);
249 return;
250 }
251
252 std::shared_ptr<OnceCallbackHolder<GetAssertionCallback>> callback_holder(
253 new OnceCallbackHolder<GetAssertionCallback>(
254 std::move(response_callback)));
255 dbus_proxy_.FidoGetAssertionAsync(
256 client_data_json, SerializeProto(request),
257 base::Bind(&OnDBusFidoGetAssertionSuccess, callback_holder),
258 base::Bind(&OnDBusFidoGetAssertionFailure, callback_holder));
259}
260
Maksim Ivanov43477a92018-12-03 04:59:27 +0100261} // namespace cryptohome