483 lines
18 KiB
C++
483 lines
18 KiB
C++
//
|
|
// 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();
|
|
}
|