// // Copyright (C) 2014 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include <stdio.h> #include <sysexits.h> #include <memory> #include <string> #include <base/command_line.h> #include <base/files/file_util.h> #include <base/message_loop/message_loop.h> #include <brillo/bind_lambda.h> #include <brillo/daemons/daemon.h> #include <brillo/syslog_logging.h> #include "attestation/client/dbus_proxy.h" #include "attestation/common/attestation_ca.pb.h" #include "attestation/common/crypto_utility_impl.h" #include "attestation/common/interface.pb.h" #include "attestation/common/print_interface_proto.h" namespace attestation { const char kCreateAndCertifyCommand[] = "create_and_certify"; const char kCreateCommand[] = "create"; const char kInfoCommand[] = "info"; const char kEndorsementCommand[] = "endorsement"; const char kAttestationKeyCommand[] = "attestation_key"; const char kActivateCommand[] = "activate"; const char kEncryptForActivateCommand[] = "encrypt_for_activate"; const char kEncryptCommand[] = "encrypt"; const char kDecryptCommand[] = "decrypt"; const char kSignCommand[] = "sign"; const char kVerifyCommand[] = "verify"; const char kRegisterCommand[] = "register"; const char kUsage[] = R"( Usage: attestation_client <command> [<args>] Commands: create_and_certify [--user=<email>] [--label=<keylabel>] Creates a key and requests certification by the Google Attestation CA. This is the default command. create [--user=<email>] [--label=<keylabel] [--usage=sign|decrypt] Creates a certifiable key. info [--user=<email>] [--label=<keylabel>] Prints info about a key. endorsement Prints info about the TPM endorsement. attestation_key Prints info about the TPM attestation key. activate --input=<input_file> Activates an attestation key using the encrypted credential in |input_file|. encrypt_for_activate --input=<input_file> --output=<output_file> Encrypts the content of |input_file| as required by the TPM for activating an attestation key. The result is written to |output_file|. encrypt [--user=<email>] [--label=<keylabel>] --input=<input_file> --output=<output_file> Encrypts the contents of |input_file| as required by the TPM for a decrypt operation. The result is written to |output_file|. decrypt [--user=<email>] [--label=<keylabel>] --input=<input_file> Decrypts the contents of |input_file|. sign [--user=<email>] [--label=<keylabel>] --input=<input_file> [--output=<output_file>] Signs the contents of |input_file|. verify [--user=<email>] [--label=<keylabel] --input=<signed_data_file> --signature=<signature_file> Verifies the signature in |signature_file| against the contents of |input_file|. register [--user=<email>] [--label=<keylabel] Registers a key with a PKCS #11 token. )"; // The Daemon class works well as a client loop as well. using ClientLoopBase = brillo::Daemon; class ClientLoop : public ClientLoopBase { public: ClientLoop() = default; ~ClientLoop() override = default; protected: int OnInit() override { int exit_code = ClientLoopBase::OnInit(); if (exit_code != EX_OK) { return exit_code; } attestation_.reset(new attestation::DBusProxy()); if (!attestation_->Initialize()) { return EX_UNAVAILABLE; } exit_code = ScheduleCommand(); if (exit_code == EX_USAGE) { printf("%s", kUsage); } return exit_code; } void OnShutdown(int* exit_code) override { attestation_.reset(); ClientLoopBase::OnShutdown(exit_code); } private: // Posts tasks according to the command line options. int ScheduleCommand() { base::Closure task; base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); const auto& args = command_line->GetArgs(); if (command_line->HasSwitch("help") || command_line->HasSwitch("h") || (!args.empty() && args.front() == "help")) { return EX_USAGE; } if (args.empty() || args.front() == kCreateAndCertifyCommand) { task = base::Bind(&ClientLoop::CallCreateGoogleAttestedKey, weak_factory_.GetWeakPtr(), command_line->GetSwitchValueASCII("label"), command_line->GetSwitchValueASCII("user")); } else if (args.front() == kCreateCommand) { std::string usage_str = command_line->GetSwitchValueASCII("usage"); KeyUsage usage; if (usage_str.empty() || usage_str == "sign") { usage = KEY_USAGE_SIGN; } else if (usage_str == "decrypt") { usage = KEY_USAGE_DECRYPT; } else { return EX_USAGE; } task = base::Bind(&ClientLoop::CallCreateCertifiableKey, weak_factory_.GetWeakPtr(), command_line->GetSwitchValueASCII("label"), command_line->GetSwitchValueASCII("user"), usage); } else if (args.front() == kInfoCommand) { task = base::Bind(&ClientLoop::CallGetKeyInfo, weak_factory_.GetWeakPtr(), command_line->GetSwitchValueASCII("label"), command_line->GetSwitchValueASCII("user")); } else if (args.front() == kEndorsementCommand) { task = base::Bind(&ClientLoop::CallGetEndorsementInfo, weak_factory_.GetWeakPtr()); } else if (args.front() == kAttestationKeyCommand) { task = base::Bind(&ClientLoop::CallGetAttestationKeyInfo, weak_factory_.GetWeakPtr()); } else if (args.front() == kActivateCommand) { if (!command_line->HasSwitch("input")) { return EX_USAGE; } std::string input; base::FilePath filename(command_line->GetSwitchValueASCII("input")); if (!base::ReadFileToString(filename, &input)) { LOG(ERROR) << "Failed to read file: " << filename.value(); return EX_NOINPUT; } task = base::Bind(&ClientLoop::CallActivateAttestationKey, weak_factory_.GetWeakPtr(), input); } else if (args.front() == kEncryptForActivateCommand) { if (!command_line->HasSwitch("input") || !command_line->HasSwitch("output")) { return EX_USAGE; } std::string input; base::FilePath filename(command_line->GetSwitchValueASCII("input")); if (!base::ReadFileToString(filename, &input)) { LOG(ERROR) << "Failed to read file: " << filename.value(); return EX_NOINPUT; } task = base::Bind(&ClientLoop::EncryptForActivate, weak_factory_.GetWeakPtr(), input); } else if (args.front() == kEncryptCommand) { if (!command_line->HasSwitch("input") || !command_line->HasSwitch("output")) { return EX_USAGE; } std::string input; base::FilePath filename(command_line->GetSwitchValueASCII("input")); if (!base::ReadFileToString(filename, &input)) { LOG(ERROR) << "Failed to read file: " << filename.value(); return EX_NOINPUT; } task = base::Bind(&ClientLoop::Encrypt, weak_factory_.GetWeakPtr(), command_line->GetSwitchValueASCII("label"), command_line->GetSwitchValueASCII("user"), input); } else if (args.front() == kDecryptCommand) { if (!command_line->HasSwitch("input")) { return EX_USAGE; } std::string input; base::FilePath filename(command_line->GetSwitchValueASCII("input")); if (!base::ReadFileToString(filename, &input)) { LOG(ERROR) << "Failed to read file: " << filename.value(); return EX_NOINPUT; } task = base::Bind(&ClientLoop::CallDecrypt, weak_factory_.GetWeakPtr(), command_line->GetSwitchValueASCII("label"), command_line->GetSwitchValueASCII("user"), input); } else if (args.front() == kSignCommand) { if (!command_line->HasSwitch("input")) { return EX_USAGE; } std::string input; base::FilePath filename(command_line->GetSwitchValueASCII("input")); if (!base::ReadFileToString(filename, &input)) { LOG(ERROR) << "Failed to read file: " << filename.value(); return EX_NOINPUT; } task = base::Bind(&ClientLoop::CallSign, weak_factory_.GetWeakPtr(), command_line->GetSwitchValueASCII("label"), command_line->GetSwitchValueASCII("user"), input); } else if (args.front() == kVerifyCommand) { if (!command_line->HasSwitch("input") || !command_line->HasSwitch("signature")) { return EX_USAGE; } std::string input; base::FilePath filename(command_line->GetSwitchValueASCII("input")); if (!base::ReadFileToString(filename, &input)) { LOG(ERROR) << "Failed to read file: " << filename.value(); return EX_NOINPUT; } std::string signature; base::FilePath filename2(command_line->GetSwitchValueASCII("signature")); if (!base::ReadFileToString(filename2, &signature)) { LOG(ERROR) << "Failed to read file: " << filename2.value(); return EX_NOINPUT; } task = base::Bind( &ClientLoop::VerifySignature, weak_factory_.GetWeakPtr(), command_line->GetSwitchValueASCII("label"), command_line->GetSwitchValueASCII("user"), input, signature); } else if (args.front() == kRegisterCommand) { task = base::Bind(&ClientLoop::CallRegister, weak_factory_.GetWeakPtr(), command_line->GetSwitchValueASCII("label"), command_line->GetSwitchValueASCII("user")); } else { return EX_USAGE; } base::MessageLoop::current()->PostTask(FROM_HERE, task); return EX_OK; } template <typename ProtobufType> void PrintReplyAndQuit(const ProtobufType& reply) { printf("%s\n", GetProtoDebugString(reply).c_str()); Quit(); } void WriteOutput(const std::string& output) { base::FilePath filename( base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII("output")); if (base::WriteFile(filename, output.data(), output.size()) != static_cast<int>(output.size())) { LOG(ERROR) << "Failed to write file: " << filename.value(); QuitWithExitCode(EX_IOERR); } } void CallCreateGoogleAttestedKey(const std::string& label, const std::string& username) { CreateGoogleAttestedKeyRequest request; request.set_key_label(label); request.set_key_type(KEY_TYPE_RSA); request.set_key_usage(KEY_USAGE_SIGN); request.set_certificate_profile(ENTERPRISE_MACHINE_CERTIFICATE); request.set_username(username); attestation_->CreateGoogleAttestedKey( request, base::Bind(&ClientLoop::PrintReplyAndQuit<CreateGoogleAttestedKeyReply>, weak_factory_.GetWeakPtr())); } void CallGetKeyInfo(const std::string& label, const std::string& username) { GetKeyInfoRequest request; request.set_key_label(label); request.set_username(username); attestation_->GetKeyInfo( request, base::Bind(&ClientLoop::PrintReplyAndQuit<GetKeyInfoReply>, weak_factory_.GetWeakPtr())); } void CallGetEndorsementInfo() { GetEndorsementInfoRequest request; request.set_key_type(KEY_TYPE_RSA); attestation_->GetEndorsementInfo( request, base::Bind(&ClientLoop::PrintReplyAndQuit<GetEndorsementInfoReply>, weak_factory_.GetWeakPtr())); } void CallGetAttestationKeyInfo() { GetAttestationKeyInfoRequest request; request.set_key_type(KEY_TYPE_RSA); attestation_->GetAttestationKeyInfo( request, base::Bind(&ClientLoop::PrintReplyAndQuit<GetAttestationKeyInfoReply>, weak_factory_.GetWeakPtr())); } void CallActivateAttestationKey(const std::string& input) { ActivateAttestationKeyRequest request; request.set_key_type(KEY_TYPE_RSA); request.mutable_encrypted_certificate()->ParseFromString(input); request.set_save_certificate(true); attestation_->ActivateAttestationKey( request, base::Bind(&ClientLoop::PrintReplyAndQuit<ActivateAttestationKeyReply>, weak_factory_.GetWeakPtr())); } void EncryptForActivate(const std::string& input) { GetEndorsementInfoRequest request; request.set_key_type(KEY_TYPE_RSA); attestation_->GetEndorsementInfo( request, base::Bind(&ClientLoop::EncryptForActivate2, weak_factory_.GetWeakPtr(), input)); } void EncryptForActivate2(const std::string& input, const GetEndorsementInfoReply& endorsement_info) { if (endorsement_info.status() != STATUS_SUCCESS) { PrintReplyAndQuit(endorsement_info); } GetAttestationKeyInfoRequest request; request.set_key_type(KEY_TYPE_RSA); attestation_->GetAttestationKeyInfo( request, base::Bind(&ClientLoop::EncryptForActivate3, weak_factory_.GetWeakPtr(), input, endorsement_info)); } void EncryptForActivate3( const std::string& input, const GetEndorsementInfoReply& endorsement_info, const GetAttestationKeyInfoReply& attestation_key_info) { if (attestation_key_info.status() != STATUS_SUCCESS) { PrintReplyAndQuit(attestation_key_info); } CryptoUtilityImpl crypto(nullptr); EncryptedIdentityCredential encrypted; if (!crypto.EncryptIdentityCredential( input, endorsement_info.ek_public_key(), attestation_key_info.public_key_tpm_format(), &encrypted)) { QuitWithExitCode(EX_SOFTWARE); } std::string output; encrypted.SerializeToString(&output); WriteOutput(output); Quit(); } void CallCreateCertifiableKey(const std::string& label, const std::string& username, KeyUsage usage) { CreateCertifiableKeyRequest request; request.set_key_label(label); request.set_username(username); request.set_key_type(KEY_TYPE_RSA); request.set_key_usage(usage); attestation_->CreateCertifiableKey( request, base::Bind(&ClientLoop::PrintReplyAndQuit<CreateCertifiableKeyReply>, weak_factory_.GetWeakPtr())); } void Encrypt(const std::string& label, const std::string& username, const std::string& input) { GetKeyInfoRequest request; request.set_key_label(label); request.set_username(username); attestation_->GetKeyInfo( request, base::Bind(&ClientLoop::Encrypt2, weak_factory_.GetWeakPtr(), input)); } void Encrypt2(const std::string& input, const GetKeyInfoReply& key_info) { CryptoUtilityImpl crypto(nullptr); std::string output; if (!crypto.EncryptForUnbind(key_info.public_key(), input, &output)) { QuitWithExitCode(EX_SOFTWARE); } WriteOutput(output); Quit(); } void CallDecrypt(const std::string& label, const std::string& username, const std::string& input) { DecryptRequest request; request.set_key_label(label); request.set_username(username); request.set_encrypted_data(input); attestation_->Decrypt( request, base::Bind(&ClientLoop::PrintReplyAndQuit<DecryptReply>, weak_factory_.GetWeakPtr())); } void CallSign(const std::string& label, const std::string& username, const std::string& input) { SignRequest request; request.set_key_label(label); request.set_username(username); request.set_data_to_sign(input); attestation_->Sign(request, base::Bind(&ClientLoop::OnSignComplete, weak_factory_.GetWeakPtr())); } void OnSignComplete(const SignReply& reply) { if (reply.status() == STATUS_SUCCESS && base::CommandLine::ForCurrentProcess()->HasSwitch("output")) { WriteOutput(reply.signature()); } PrintReplyAndQuit<SignReply>(reply); } void VerifySignature(const std::string& label, const std::string& username, const std::string& input, const std::string& signature) { GetKeyInfoRequest request; request.set_key_label(label); request.set_username(username); attestation_->GetKeyInfo( request, base::Bind(&ClientLoop::VerifySignature2, weak_factory_.GetWeakPtr(), input, signature)); } void VerifySignature2(const std::string& input, const std::string& signature, const GetKeyInfoReply& key_info) { CryptoUtilityImpl crypto(nullptr); if (crypto.VerifySignature(key_info.public_key(), input, signature)) { printf("Signature is OK!\n"); } else { printf("Signature is BAD!\n"); } Quit(); } void CallRegister(const std::string& label, const std::string& username) { RegisterKeyWithChapsTokenRequest request; request.set_key_label(label); request.set_username(username); attestation_->RegisterKeyWithChapsToken( request, base::Bind( &ClientLoop::PrintReplyAndQuit<RegisterKeyWithChapsTokenReply>, weak_factory_.GetWeakPtr())); } std::unique_ptr<attestation::AttestationInterface> attestation_; // Declare this last so weak pointers will be destroyed first. base::WeakPtrFactory<ClientLoop> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(ClientLoop); }; } // namespace attestation int main(int argc, char* argv[]) { base::CommandLine::Init(argc, argv); brillo::InitLog(brillo::kLogToStderr); attestation::ClientLoop loop; return loop.Run(); }