You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
18 KiB
483 lines
18 KiB
// |
|
// 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(); |
|
}
|
|
|