1 //
2 // Copyright (C) 2014 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <stdio.h>
18 #include <sysexits.h>
19
20 #include <memory>
21 #include <string>
22
23 #include <base/command_line.h>
24 #include <base/files/file_util.h>
25 #include <base/message_loop/message_loop.h>
26 #include <brillo/bind_lambda.h>
27 #include <brillo/daemons/daemon.h>
28 #include <brillo/syslog_logging.h>
29
30 #include "attestation/client/dbus_proxy.h"
31 #include "attestation/common/attestation_ca.pb.h"
32 #include "attestation/common/crypto_utility_impl.h"
33 #include "attestation/common/interface.pb.h"
34 #include "attestation/common/print_interface_proto.h"
35
36 namespace attestation {
37
38 const char kCreateAndCertifyCommand[] = "create_and_certify";
39 const char kCreateCommand[] = "create";
40 const char kInfoCommand[] = "info";
41 const char kEndorsementCommand[] = "endorsement";
42 const char kAttestationKeyCommand[] = "attestation_key";
43 const char kActivateCommand[] = "activate";
44 const char kEncryptForActivateCommand[] = "encrypt_for_activate";
45 const char kEncryptCommand[] = "encrypt";
46 const char kDecryptCommand[] = "decrypt";
47 const char kSignCommand[] = "sign";
48 const char kVerifyCommand[] = "verify";
49 const char kRegisterCommand[] = "register";
50 const char kUsage[] = R"(
51 Usage: attestation_client <command> [<args>]
52 Commands:
53 create_and_certify [--user=<email>] [--label=<keylabel>]
54 Creates a key and requests certification by the Google Attestation CA.
55 This is the default command.
56 create [--user=<email>] [--label=<keylabel] [--usage=sign|decrypt]
57 Creates a certifiable key.
58
59 info [--user=<email>] [--label=<keylabel>]
60 Prints info about a key.
61 endorsement
62 Prints info about the TPM endorsement.
63 attestation_key
64 Prints info about the TPM attestation key.
65
66 activate --input=<input_file>
67 Activates an attestation key using the encrypted credential in
68 |input_file|.
69 encrypt_for_activate --input=<input_file> --output=<output_file>
70 Encrypts the content of |input_file| as required by the TPM for activating
71 an attestation key. The result is written to |output_file|.
72
73 encrypt [--user=<email>] [--label=<keylabel>] --input=<input_file>
74 --output=<output_file>
75 Encrypts the contents of |input_file| as required by the TPM for a decrypt
76 operation. The result is written to |output_file|.
77 decrypt [--user=<email>] [--label=<keylabel>] --input=<input_file>
78 Decrypts the contents of |input_file|.
79
80 sign [--user=<email>] [--label=<keylabel>] --input=<input_file>
81 [--output=<output_file>]
82 Signs the contents of |input_file|.
83 verify [--user=<email>] [--label=<keylabel] --input=<signed_data_file>
84 --signature=<signature_file>
85 Verifies the signature in |signature_file| against the contents of
86 |input_file|.
87
88 register [--user=<email>] [--label=<keylabel]
89 Registers a key with a PKCS #11 token.
90 )";
91
92 // The Daemon class works well as a client loop as well.
93 using ClientLoopBase = brillo::Daemon;
94
95 class ClientLoop : public ClientLoopBase {
96 public:
97 ClientLoop() = default;
98 ~ClientLoop() override = default;
99
100 protected:
OnInit()101 int OnInit() override {
102 int exit_code = ClientLoopBase::OnInit();
103 if (exit_code != EX_OK) {
104 return exit_code;
105 }
106 attestation_.reset(new attestation::DBusProxy());
107 if (!attestation_->Initialize()) {
108 return EX_UNAVAILABLE;
109 }
110 exit_code = ScheduleCommand();
111 if (exit_code == EX_USAGE) {
112 printf("%s", kUsage);
113 }
114 return exit_code;
115 }
116
OnShutdown(int * exit_code)117 void OnShutdown(int* exit_code) override {
118 attestation_.reset();
119 ClientLoopBase::OnShutdown(exit_code);
120 }
121
122 private:
123 // Posts tasks according to the command line options.
ScheduleCommand()124 int ScheduleCommand() {
125 base::Closure task;
126 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
127 const auto& args = command_line->GetArgs();
128 if (command_line->HasSwitch("help") || command_line->HasSwitch("h") ||
129 (!args.empty() && args.front() == "help")) {
130 return EX_USAGE;
131 }
132 if (args.empty() || args.front() == kCreateAndCertifyCommand) {
133 task = base::Bind(&ClientLoop::CallCreateGoogleAttestedKey,
134 weak_factory_.GetWeakPtr(),
135 command_line->GetSwitchValueASCII("label"),
136 command_line->GetSwitchValueASCII("user"));
137 } else if (args.front() == kCreateCommand) {
138 std::string usage_str = command_line->GetSwitchValueASCII("usage");
139 KeyUsage usage;
140 if (usage_str.empty() || usage_str == "sign") {
141 usage = KEY_USAGE_SIGN;
142 } else if (usage_str == "decrypt") {
143 usage = KEY_USAGE_DECRYPT;
144 } else {
145 return EX_USAGE;
146 }
147 task = base::Bind(&ClientLoop::CallCreateCertifiableKey,
148 weak_factory_.GetWeakPtr(),
149 command_line->GetSwitchValueASCII("label"),
150 command_line->GetSwitchValueASCII("user"), usage);
151 } else if (args.front() == kInfoCommand) {
152 task = base::Bind(&ClientLoop::CallGetKeyInfo, weak_factory_.GetWeakPtr(),
153 command_line->GetSwitchValueASCII("label"),
154 command_line->GetSwitchValueASCII("user"));
155 } else if (args.front() == kEndorsementCommand) {
156 task = base::Bind(&ClientLoop::CallGetEndorsementInfo,
157 weak_factory_.GetWeakPtr());
158 } else if (args.front() == kAttestationKeyCommand) {
159 task = base::Bind(&ClientLoop::CallGetAttestationKeyInfo,
160 weak_factory_.GetWeakPtr());
161 } else if (args.front() == kActivateCommand) {
162 if (!command_line->HasSwitch("input")) {
163 return EX_USAGE;
164 }
165 std::string input;
166 base::FilePath filename(command_line->GetSwitchValueASCII("input"));
167 if (!base::ReadFileToString(filename, &input)) {
168 LOG(ERROR) << "Failed to read file: " << filename.value();
169 return EX_NOINPUT;
170 }
171 task = base::Bind(&ClientLoop::CallActivateAttestationKey,
172 weak_factory_.GetWeakPtr(), input);
173 } else if (args.front() == kEncryptForActivateCommand) {
174 if (!command_line->HasSwitch("input") ||
175 !command_line->HasSwitch("output")) {
176 return EX_USAGE;
177 }
178 std::string input;
179 base::FilePath filename(command_line->GetSwitchValueASCII("input"));
180 if (!base::ReadFileToString(filename, &input)) {
181 LOG(ERROR) << "Failed to read file: " << filename.value();
182 return EX_NOINPUT;
183 }
184 task = base::Bind(&ClientLoop::EncryptForActivate,
185 weak_factory_.GetWeakPtr(), input);
186 } else if (args.front() == kEncryptCommand) {
187 if (!command_line->HasSwitch("input") ||
188 !command_line->HasSwitch("output")) {
189 return EX_USAGE;
190 }
191 std::string input;
192 base::FilePath filename(command_line->GetSwitchValueASCII("input"));
193 if (!base::ReadFileToString(filename, &input)) {
194 LOG(ERROR) << "Failed to read file: " << filename.value();
195 return EX_NOINPUT;
196 }
197 task = base::Bind(&ClientLoop::Encrypt, weak_factory_.GetWeakPtr(),
198 command_line->GetSwitchValueASCII("label"),
199 command_line->GetSwitchValueASCII("user"), input);
200 } else if (args.front() == kDecryptCommand) {
201 if (!command_line->HasSwitch("input")) {
202 return EX_USAGE;
203 }
204 std::string input;
205 base::FilePath filename(command_line->GetSwitchValueASCII("input"));
206 if (!base::ReadFileToString(filename, &input)) {
207 LOG(ERROR) << "Failed to read file: " << filename.value();
208 return EX_NOINPUT;
209 }
210 task = base::Bind(&ClientLoop::CallDecrypt, weak_factory_.GetWeakPtr(),
211 command_line->GetSwitchValueASCII("label"),
212 command_line->GetSwitchValueASCII("user"), input);
213 } else if (args.front() == kSignCommand) {
214 if (!command_line->HasSwitch("input")) {
215 return EX_USAGE;
216 }
217 std::string input;
218 base::FilePath filename(command_line->GetSwitchValueASCII("input"));
219 if (!base::ReadFileToString(filename, &input)) {
220 LOG(ERROR) << "Failed to read file: " << filename.value();
221 return EX_NOINPUT;
222 }
223 task = base::Bind(&ClientLoop::CallSign, weak_factory_.GetWeakPtr(),
224 command_line->GetSwitchValueASCII("label"),
225 command_line->GetSwitchValueASCII("user"), input);
226 } else if (args.front() == kVerifyCommand) {
227 if (!command_line->HasSwitch("input") ||
228 !command_line->HasSwitch("signature")) {
229 return EX_USAGE;
230 }
231 std::string input;
232 base::FilePath filename(command_line->GetSwitchValueASCII("input"));
233 if (!base::ReadFileToString(filename, &input)) {
234 LOG(ERROR) << "Failed to read file: " << filename.value();
235 return EX_NOINPUT;
236 }
237 std::string signature;
238 base::FilePath filename2(command_line->GetSwitchValueASCII("signature"));
239 if (!base::ReadFileToString(filename2, &signature)) {
240 LOG(ERROR) << "Failed to read file: " << filename2.value();
241 return EX_NOINPUT;
242 }
243 task = base::Bind(
244 &ClientLoop::VerifySignature, weak_factory_.GetWeakPtr(),
245 command_line->GetSwitchValueASCII("label"),
246 command_line->GetSwitchValueASCII("user"), input, signature);
247 } else if (args.front() == kRegisterCommand) {
248 task = base::Bind(&ClientLoop::CallRegister, weak_factory_.GetWeakPtr(),
249 command_line->GetSwitchValueASCII("label"),
250 command_line->GetSwitchValueASCII("user"));
251 } else {
252 return EX_USAGE;
253 }
254 base::MessageLoop::current()->PostTask(FROM_HERE, task);
255 return EX_OK;
256 }
257
258 template <typename ProtobufType>
PrintReplyAndQuit(const ProtobufType & reply)259 void PrintReplyAndQuit(const ProtobufType& reply) {
260 printf("%s\n", GetProtoDebugString(reply).c_str());
261 Quit();
262 }
263
WriteOutput(const std::string & output)264 void WriteOutput(const std::string& output) {
265 base::FilePath filename(
266 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII("output"));
267 if (base::WriteFile(filename, output.data(), output.size()) !=
268 static_cast<int>(output.size())) {
269 LOG(ERROR) << "Failed to write file: " << filename.value();
270 QuitWithExitCode(EX_IOERR);
271 }
272 }
273
CallCreateGoogleAttestedKey(const std::string & label,const std::string & username)274 void CallCreateGoogleAttestedKey(const std::string& label,
275 const std::string& username) {
276 CreateGoogleAttestedKeyRequest request;
277 request.set_key_label(label);
278 request.set_key_type(KEY_TYPE_RSA);
279 request.set_key_usage(KEY_USAGE_SIGN);
280 request.set_certificate_profile(ENTERPRISE_MACHINE_CERTIFICATE);
281 request.set_username(username);
282 attestation_->CreateGoogleAttestedKey(
283 request,
284 base::Bind(&ClientLoop::PrintReplyAndQuit<CreateGoogleAttestedKeyReply>,
285 weak_factory_.GetWeakPtr()));
286 }
287
CallGetKeyInfo(const std::string & label,const std::string & username)288 void CallGetKeyInfo(const std::string& label, const std::string& username) {
289 GetKeyInfoRequest request;
290 request.set_key_label(label);
291 request.set_username(username);
292 attestation_->GetKeyInfo(
293 request, base::Bind(&ClientLoop::PrintReplyAndQuit<GetKeyInfoReply>,
294 weak_factory_.GetWeakPtr()));
295 }
296
CallGetEndorsementInfo()297 void CallGetEndorsementInfo() {
298 GetEndorsementInfoRequest request;
299 request.set_key_type(KEY_TYPE_RSA);
300 attestation_->GetEndorsementInfo(
301 request,
302 base::Bind(&ClientLoop::PrintReplyAndQuit<GetEndorsementInfoReply>,
303 weak_factory_.GetWeakPtr()));
304 }
305
CallGetAttestationKeyInfo()306 void CallGetAttestationKeyInfo() {
307 GetAttestationKeyInfoRequest request;
308 request.set_key_type(KEY_TYPE_RSA);
309 attestation_->GetAttestationKeyInfo(
310 request,
311 base::Bind(&ClientLoop::PrintReplyAndQuit<GetAttestationKeyInfoReply>,
312 weak_factory_.GetWeakPtr()));
313 }
314
CallActivateAttestationKey(const std::string & input)315 void CallActivateAttestationKey(const std::string& input) {
316 ActivateAttestationKeyRequest request;
317 request.set_key_type(KEY_TYPE_RSA);
318 request.mutable_encrypted_certificate()->ParseFromString(input);
319 request.set_save_certificate(true);
320 attestation_->ActivateAttestationKey(
321 request,
322 base::Bind(&ClientLoop::PrintReplyAndQuit<ActivateAttestationKeyReply>,
323 weak_factory_.GetWeakPtr()));
324 }
325
EncryptForActivate(const std::string & input)326 void EncryptForActivate(const std::string& input) {
327 GetEndorsementInfoRequest request;
328 request.set_key_type(KEY_TYPE_RSA);
329 attestation_->GetEndorsementInfo(
330 request, base::Bind(&ClientLoop::EncryptForActivate2,
331 weak_factory_.GetWeakPtr(), input));
332 }
333
EncryptForActivate2(const std::string & input,const GetEndorsementInfoReply & endorsement_info)334 void EncryptForActivate2(const std::string& input,
335 const GetEndorsementInfoReply& endorsement_info) {
336 if (endorsement_info.status() != STATUS_SUCCESS) {
337 PrintReplyAndQuit(endorsement_info);
338 }
339 GetAttestationKeyInfoRequest request;
340 request.set_key_type(KEY_TYPE_RSA);
341 attestation_->GetAttestationKeyInfo(
342 request,
343 base::Bind(&ClientLoop::EncryptForActivate3, weak_factory_.GetWeakPtr(),
344 input, endorsement_info));
345 }
346
EncryptForActivate3(const std::string & input,const GetEndorsementInfoReply & endorsement_info,const GetAttestationKeyInfoReply & attestation_key_info)347 void EncryptForActivate3(
348 const std::string& input,
349 const GetEndorsementInfoReply& endorsement_info,
350 const GetAttestationKeyInfoReply& attestation_key_info) {
351 if (attestation_key_info.status() != STATUS_SUCCESS) {
352 PrintReplyAndQuit(attestation_key_info);
353 }
354 CryptoUtilityImpl crypto(nullptr);
355 EncryptedIdentityCredential encrypted;
356 if (!crypto.EncryptIdentityCredential(
357 input, endorsement_info.ek_public_key(),
358 attestation_key_info.public_key_tpm_format(), &encrypted)) {
359 QuitWithExitCode(EX_SOFTWARE);
360 }
361 std::string output;
362 encrypted.SerializeToString(&output);
363 WriteOutput(output);
364 Quit();
365 }
366
CallCreateCertifiableKey(const std::string & label,const std::string & username,KeyUsage usage)367 void CallCreateCertifiableKey(const std::string& label,
368 const std::string& username,
369 KeyUsage usage) {
370 CreateCertifiableKeyRequest request;
371 request.set_key_label(label);
372 request.set_username(username);
373 request.set_key_type(KEY_TYPE_RSA);
374 request.set_key_usage(usage);
375 attestation_->CreateCertifiableKey(
376 request,
377 base::Bind(&ClientLoop::PrintReplyAndQuit<CreateCertifiableKeyReply>,
378 weak_factory_.GetWeakPtr()));
379 }
380
Encrypt(const std::string & label,const std::string & username,const std::string & input)381 void Encrypt(const std::string& label,
382 const std::string& username,
383 const std::string& input) {
384 GetKeyInfoRequest request;
385 request.set_key_label(label);
386 request.set_username(username);
387 attestation_->GetKeyInfo(
388 request,
389 base::Bind(&ClientLoop::Encrypt2, weak_factory_.GetWeakPtr(), input));
390 }
391
Encrypt2(const std::string & input,const GetKeyInfoReply & key_info)392 void Encrypt2(const std::string& input, const GetKeyInfoReply& key_info) {
393 CryptoUtilityImpl crypto(nullptr);
394 std::string output;
395 if (!crypto.EncryptForUnbind(key_info.public_key(), input, &output)) {
396 QuitWithExitCode(EX_SOFTWARE);
397 }
398 WriteOutput(output);
399 Quit();
400 }
401
CallDecrypt(const std::string & label,const std::string & username,const std::string & input)402 void CallDecrypt(const std::string& label,
403 const std::string& username,
404 const std::string& input) {
405 DecryptRequest request;
406 request.set_key_label(label);
407 request.set_username(username);
408 request.set_encrypted_data(input);
409 attestation_->Decrypt(
410 request, base::Bind(&ClientLoop::PrintReplyAndQuit<DecryptReply>,
411 weak_factory_.GetWeakPtr()));
412 }
413
CallSign(const std::string & label,const std::string & username,const std::string & input)414 void CallSign(const std::string& label,
415 const std::string& username,
416 const std::string& input) {
417 SignRequest request;
418 request.set_key_label(label);
419 request.set_username(username);
420 request.set_data_to_sign(input);
421 attestation_->Sign(request, base::Bind(&ClientLoop::OnSignComplete,
422 weak_factory_.GetWeakPtr()));
423 }
424
OnSignComplete(const SignReply & reply)425 void OnSignComplete(const SignReply& reply) {
426 if (reply.status() == STATUS_SUCCESS &&
427 base::CommandLine::ForCurrentProcess()->HasSwitch("output")) {
428 WriteOutput(reply.signature());
429 }
430 PrintReplyAndQuit<SignReply>(reply);
431 }
432
VerifySignature(const std::string & label,const std::string & username,const std::string & input,const std::string & signature)433 void VerifySignature(const std::string& label,
434 const std::string& username,
435 const std::string& input,
436 const std::string& signature) {
437 GetKeyInfoRequest request;
438 request.set_key_label(label);
439 request.set_username(username);
440 attestation_->GetKeyInfo(
441 request, base::Bind(&ClientLoop::VerifySignature2,
442 weak_factory_.GetWeakPtr(), input, signature));
443 }
444
VerifySignature2(const std::string & input,const std::string & signature,const GetKeyInfoReply & key_info)445 void VerifySignature2(const std::string& input,
446 const std::string& signature,
447 const GetKeyInfoReply& key_info) {
448 CryptoUtilityImpl crypto(nullptr);
449 if (crypto.VerifySignature(key_info.public_key(), input, signature)) {
450 printf("Signature is OK!\n");
451 } else {
452 printf("Signature is BAD!\n");
453 }
454 Quit();
455 }
456
CallRegister(const std::string & label,const std::string & username)457 void CallRegister(const std::string& label, const std::string& username) {
458 RegisterKeyWithChapsTokenRequest request;
459 request.set_key_label(label);
460 request.set_username(username);
461 attestation_->RegisterKeyWithChapsToken(
462 request,
463 base::Bind(
464 &ClientLoop::PrintReplyAndQuit<RegisterKeyWithChapsTokenReply>,
465 weak_factory_.GetWeakPtr()));
466 }
467
468 std::unique_ptr<attestation::AttestationInterface> attestation_;
469
470 // Declare this last so weak pointers will be destroyed first.
471 base::WeakPtrFactory<ClientLoop> weak_factory_{this};
472
473 DISALLOW_COPY_AND_ASSIGN(ClientLoop);
474 };
475
476 } // namespace attestation
477
main(int argc,char * argv[])478 int main(int argc, char* argv[]) {
479 base::CommandLine::Init(argc, argv);
480 brillo::InitLog(brillo::kLogToStderr);
481 attestation::ClientLoop loop;
482 return loop.Run();
483 }
484