blob: 6cf9752f7b710d87afbe5c98150b7e97e2c46229 [file] [log] [blame] [edit]
// Copyright 2014 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <sysexits.h>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <attestation/proto_bindings/attestation_ca.pb.h>
#include <attestation/proto_bindings/interface.pb.h>
#include <attestation-client/attestation/dbus-proxies.h>
#include <base/check.h>
#include <base/command_line.h>
#include <base/files/file_util.h>
#include <base/functional/bind.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/task/single_thread_task_runner.h>
#include <brillo/daemons/daemon.h>
#include <brillo/dbus/dbus_connection.h>
#include <brillo/syslog_logging.h>
#include <libhwsec/factory/factory_impl.h>
#include <libhwsec/frontend/attestation/frontend.h>
#include <libhwsec-foundation/tpm/tpm_version.h>
#include "attestation/common/crypto_utility_impl.h"
#include "attestation/common/print_interface_proto.h"
namespace attestation {
namespace {
// The Daemon class works well as a client loop as well.
using ClientLoopBase = brillo::Daemon;
// Certificate profile specific request data. Loosely corresponds to `oneof`
// the proto fields at `GetCertificateRequest::metadata` in
// `dbus/attestation/interface.proto`. `CertProfileSpecificData` itself is
// equivalent to a type-safe tagged union type that can represent any of the
// types inside the `std::variant`.
using CertProfileSpecificData =
std::variant<DeviceSetupCertificateRequestMetadata>;
constexpr base::TimeDelta kDefaultTimeout = base::Minutes(5);
const char kGetFeaturesCommand[] = "features";
const char kCreateCommand[] = "create";
const char kInfoCommand[] = "info";
const char kSetKeyPayloadCommand[] = "set_key_payload";
const char kDeleteKeysCommand[] = "delete_keys";
const char kEndorsementCommand[] = "endorsement";
const char kAttestationKeyCommand[] = "attestation_key";
const char kVerifyAttestationCommand[] = "verify_attestation";
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 kStatusCommand[] = "status";
const char kCreateEnrollRequestCommand[] = "create_enroll_request";
const char kFinishEnrollCommand[] = "finish_enroll";
const char kEnrollCommand[] = "enroll";
const char kCreateCertRequestCommand[] = "create_cert_request";
const char kFinishCertRequestCommand[] = "finish_cert_request";
const char kGetCertCommand[] = "get_cert";
const char kSignChallengeCommand[] = "sign_challenge";
const char kGetEnrollmentId[] = "get_enrollment_id";
const char kGetCertifiedNvIndex[] = "get_certified_nv_index";
const char kDeviceSetupCertId[] = "device_setup_cert_id";
const char kDeviceSetupCertContentBinding[] =
"device_setup_cert_content_binding";
const char kUsage[] = R"(
Usage: attestation_client <command> [<args>]
Commands:
features
Prints the features returned by attestation service.
create [--user=<email>] [--label=<keylabel>] [--usage=sign|decrypt]
Creates a certifiable key.
set_key_payload [--user=<email>] --label=<keylabel> --input=<input_file>
Reads payload from |input_file| and sets it for the specified key.
delete_keys [--user=<email>] --prefix=<prefix>
Deletes all keys with the specified |prefix|.
status [--extended]
Requests and prints status or extended status: prepared_for_enrollment,
enrolled, verified_boot [extended].
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.
verify_attestation [--ek-only] [--cros-core]
Verifies attestation information. If |ek-only| flag is provided,
verifies only the endorsement key. If |cros-core| flag is provided,
verifies using CrosCore CA public key.
activate [--attestation-server=default|test] --input=<input_file> [--save]
Activates an attestation key using the encrypted credential in
|input_file| and optionally saves it for future certifications.
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|.
create_enroll_request [--attestation-server=default|test]
[--output=<output_file>]
Creates enroll request to CA and stores it to |output_file|.
finish_enroll [--attestation-server=default|test] --input=<input_file>
Finishes enrollment using the CA response from |input_file|.
enroll [--attestation-server=default|test] [--forced]
Enrolls the device to the specified CA.
create_cert_request [--attestation-server=default|test]
[--profile=<profile>] [--user=<user>] [--origin=<origin>]
[--key-type={rsa|ecc}] [--output=<output_file>]
[--device_setup_cert_id=<An id for the cert; usually device id>]
[--device_setup_cert_content_binding=<A unique string, e.g. timestamp>]
Creates certificate request to CA for |user|, using provided certificate
|profile| and |origin|, and stores it to |output_file|.
Possible |profile| values: user, machine, enrollment, content, cpsi, cast,
gfsc, device_setup. Default is user.
|device_setup_cert_id| and |device_setup_cert_content_binding| are
required if |profile| is "device_setup".
finish_cert_request [--attestation-server=default|test] [--user=<user>]
[--label=<label>] --input=<input_file>
Finishes certificate request for |user| using the CA response from
|input_file|, and stores it in the key with the specified |label|.
get_cert [--attestation-server=default|test] [--profile=<profile>]
[--label=<label>] [--user=<user>] [--origin=<origin>]
[--output=<output_file>] [--key-type={rsa|ecc}] [--forced]
[--device_setup_cert_id=<An id for the cert; usually device id>]
[--device_setup_cert_content_binding=<A unique string, e.g. timestamp>]
Creates certificate request to CA for |user|, using provided certificate
|profile| and |origin|, and sends to the specified CA, then stores it
with the specified |label|.
Possible |profile| values: user, machine, enrollment, content, cpsi,
cast, gfsc, device_setup. Default is user.
|device_setup_cert_id| and |device_setup_cert_content_binding| are
required if |profile| is "device_setup".
sign_challenge [--enterprise [--va_server=default|test]] [--user=<user>]
[--label=<label>] [--domain=<domain>] [--device_id=<device_id>]
[--spkac] --input=<input_file> [--output=<output_file>]
Signs a challenge (EnterpriseChallenge, if |enterprise| flag is given,
otherwise a SimpleChallenge) provided in the |input_file|. Stores
the response in the |output_file|, if specified.
register [--user=<email>] [--label=<keylabel]
Registers a key with a PKCS #11 token.
get_enrollment_id [--ignore_cache]
Returns the enrollment ID. If ignore_cache option is provided, the ID is
computed and the cache is not used to read, nor to update the value.
Otherwise the value from cache is returned if present.
get_certified_nv_index [--index=<nv_index>] [--size=<bytes>]
[--key=<key_label>] [--output=<output_file>]
Returns a copy of the specified NV index, certified by the specified
key, eg "attest-ent-machine".
)";
// Reads parameters and `command_line` and optionally returns
// `CertProfileSpecificData` for `DEVICE_SETUP_CERTIFICATE`. Returns an empty
// optional if `command_line` does not contain the flags required for
// constructing `CertProfileSpecificData`.
std::optional<CertProfileSpecificData> CreateDeviceSetupProfileSpecificData(
const base::CommandLine* command_line) {
if (!command_line->HasSwitch(kDeviceSetupCertId)) {
return std::nullopt;
}
if (!command_line->HasSwitch(kDeviceSetupCertContentBinding)) {
return std::nullopt;
}
DeviceSetupCertificateRequestMetadata metadata;
metadata.set_id(command_line->GetSwitchValueASCII(kDeviceSetupCertId));
metadata.set_content_binding(
command_line->GetSwitchValueASCII(kDeviceSetupCertContentBinding));
return std::make_optional(CertProfileSpecificData(metadata));
}
std::optional<CertificateProfile> ToCertificateProfile(
const std::string& profile) {
if (profile.empty() || profile == "enterprise_user" || profile == "user" ||
profile == "u") {
return ENTERPRISE_USER_CERTIFICATE;
}
if (profile == "enterprise_machine" || profile == "machine" ||
profile == "m") {
return ENTERPRISE_MACHINE_CERTIFICATE;
}
if (profile == "enterprise_enrollment" || profile == "enrollment" ||
profile == "e") {
return ENTERPRISE_ENROLLMENT_CERTIFICATE;
}
if (profile == "content_protection" || profile == "content" ||
profile == "c") {
return CONTENT_PROTECTION_CERTIFICATE;
}
if (profile == "content_protection_with_stable_id" || profile == "cpsi") {
return CONTENT_PROTECTION_CERTIFICATE_WITH_STABLE_ID;
}
if (profile == "cast") {
return CAST_CERTIFICATE;
}
if (profile == "gfsc") {
return GFSC_CERTIFICATE;
}
if (profile == "vtpm_ek" || profile == "vtpm") {
return ENTERPRISE_VTPM_EK_CERTIFICATE;
}
if (profile == "arc_dk") {
return ARC_ATTESTATION_DEVICE_KEY_CERTIFICATE;
}
if (profile == "arc_tpm") {
return ARC_TPM_CERTIFYING_KEY_CERTIFICATE;
}
if (profile == "device_setup") {
return DEVICE_SETUP_CERTIFICATE;
}
return std::nullopt;
}
} // namespace
class ClientLoop : public ClientLoopBase {
public:
ClientLoop() = default;
ClientLoop(const ClientLoop&) = delete;
ClientLoop& operator=(const ClientLoop&) = delete;
~ClientLoop() override = default;
protected:
int OnInit() override {
int exit_code = ClientLoopBase::OnInit();
if (exit_code != EX_OK) {
return exit_code;
}
scoped_refptr<dbus::Bus> bus = connection_.Connect();
CHECK(bus) << "Failed to connect to system D-Bus";
attestation_ = std::make_unique<org::chromium::AttestationProxy>(bus);
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::OnceClosure 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.empty() && args.front() == "help")) {
return EX_USAGE;
} else if (args.front() == kGetFeaturesCommand) {
task = base::BindOnce(&ClientLoop::CallGetFeatures,
weak_factory_.GetWeakPtr());
} 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::BindOnce(&ClientLoop::CallCreateCertifiableKey,
weak_factory_.GetWeakPtr(),
command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"), usage);
} else if (args.front() == kStatusCommand) {
task =
base::BindOnce(&ClientLoop::CallGetStatus, weak_factory_.GetWeakPtr(),
command_line->HasSwitch("extended"));
} else if (args.front() == kInfoCommand) {
task = base::BindOnce(&ClientLoop::CallGetKeyInfo,
weak_factory_.GetWeakPtr(),
command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"));
} else if (args.front() == kSetKeyPayloadCommand) {
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::BindOnce(&ClientLoop::CallSetKeyPayload,
weak_factory_.GetWeakPtr(), input,
command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"));
} else if (args.front() == kDeleteKeysCommand) {
if (command_line->HasSwitch("label") &&
command_line->HasSwitch("prefix")) {
return EX_USAGE;
}
task = base::BindOnce(&ClientLoop::CallDeleteKeys,
weak_factory_.GetWeakPtr(),
command_line->GetSwitchValueASCII("prefix"),
command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"));
} else if (args.front() == kEndorsementCommand) {
task = base::BindOnce(&ClientLoop::CallGetEndorsementInfo,
weak_factory_.GetWeakPtr());
} else if (args.front() == kAttestationKeyCommand) {
ACAType aca_type;
int status = GetCertificateAuthorityServerType(command_line, &aca_type);
if (status != EX_OK) {
return status;
}
task = base::BindOnce(&ClientLoop::CallGetAttestationKeyInfo,
weak_factory_.GetWeakPtr(), aca_type);
} else if (args.front() == kVerifyAttestationCommand) {
task = base::BindOnce(&ClientLoop::CallVerifyAttestation,
weak_factory_.GetWeakPtr(),
command_line->HasSwitch("cros-core"),
command_line->HasSwitch("ek-only"));
} else if (args.front() == kActivateCommand) {
ACAType aca_type;
int status = GetCertificateAuthorityServerType(command_line, &aca_type);
if (status != EX_OK) {
return status;
}
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::BindOnce(&ClientLoop::CallActivateAttestationKey,
weak_factory_.GetWeakPtr(), aca_type, input,
command_line->HasSwitch("save"));
} 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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(
&ClientLoop::VerifySignature, weak_factory_.GetWeakPtr(),
command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"), input, signature);
} else if (args.front() == kRegisterCommand) {
task =
base::BindOnce(&ClientLoop::CallRegister, weak_factory_.GetWeakPtr(),
command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"));
} else if (args.front() == kCreateEnrollRequestCommand) {
ACAType aca_type;
int status = GetCertificateAuthorityServerType(command_line, &aca_type);
if (status != EX_OK) {
return status;
}
task = base::BindOnce(&ClientLoop::CallCreateEnrollRequest,
weak_factory_.GetWeakPtr(), aca_type);
} else if (args.front() == kFinishEnrollCommand) {
ACAType aca_type;
int status = GetCertificateAuthorityServerType(command_line, &aca_type);
if (status != EX_OK) {
return status;
}
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::BindOnce(&ClientLoop::CallFinishEnroll,
weak_factory_.GetWeakPtr(), aca_type, input);
} else if (args.front() == kEnrollCommand) {
ACAType aca_type;
int status = GetCertificateAuthorityServerType(command_line, &aca_type);
if (status != EX_OK) {
return status;
}
bool forced = command_line->HasSwitch("forced");
task = base::BindOnce(&ClientLoop::CallEnroll, weak_factory_.GetWeakPtr(),
aca_type, forced);
} else if (args.front() == kCreateCertRequestCommand) {
ACAType aca_type;
int status = GetCertificateAuthorityServerType(command_line, &aca_type);
if (status != EX_OK) {
return status;
}
KeyType key_type;
status = GetKeyType(command_line, &key_type);
if (status != EX_OK) {
return status;
}
std::optional<CertificateProfile> profile =
ToCertificateProfile(command_line->GetSwitchValueASCII("profile"));
if (!profile.has_value()) {
return EX_USAGE;
}
std::optional<CertProfileSpecificData> profile_specific_data;
if (profile == DEVICE_SETUP_CERTIFICATE) {
profile_specific_data =
CreateDeviceSetupProfileSpecificData(command_line);
if (!profile_specific_data) {
return EX_USAGE;
}
}
task = base::BindOnce(&ClientLoop::CallCreateCertRequest,
weak_factory_.GetWeakPtr(), aca_type, *profile,
command_line->GetSwitchValueASCII("user"),
command_line->GetSwitchValueASCII("origin"),
key_type, profile_specific_data);
} else if (args.front() == kFinishCertRequestCommand) {
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::BindOnce(&ClientLoop::CallFinishCertRequest,
weak_factory_.GetWeakPtr(), input,
command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"));
} else if (args.front() == kGetCertCommand) {
ACAType aca_type;
int status = GetCertificateAuthorityServerType(command_line, &aca_type);
if (status != EX_OK) {
return status;
}
KeyType key_type;
status = GetKeyType(command_line, &key_type);
if (status != EX_OK) {
return status;
}
std::optional<CertificateProfile> profile =
ToCertificateProfile(command_line->GetSwitchValueASCII("profile"));
if (!profile.has_value()) {
return EX_USAGE;
}
bool forced = command_line->HasSwitch("forced");
bool shall_trigger_enrollment = command_line->HasSwitch("enroll");
std::optional<CertProfileSpecificData> profile_specific_data;
if (profile == DEVICE_SETUP_CERTIFICATE) {
profile_specific_data =
CreateDeviceSetupProfileSpecificData(command_line);
if (!profile_specific_data) {
return EX_USAGE;
}
}
task = base::BindOnce(
&ClientLoop::CallGetCert, weak_factory_.GetWeakPtr(), aca_type,
*profile, command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"),
command_line->GetSwitchValueASCII("origin"), key_type, forced,
shall_trigger_enrollment, profile_specific_data);
} else if (args.front() == kSignChallengeCommand) {
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;
}
if (command_line->HasSwitch("enterprise")) {
VAType va_type;
int status = GetVerifiedAccessServerType(command_line, &va_type);
if (status != EX_OK) {
return status;
}
task = base::BindOnce(&ClientLoop::CallSignEnterpriseChallenge,
weak_factory_.GetWeakPtr(), va_type, input,
command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"),
command_line->GetSwitchValueASCII("domain"),
command_line->GetSwitchValueASCII("device_id"),
command_line->HasSwitch("spkac"));
} else {
task = base::BindOnce(&ClientLoop::CallSignSimpleChallenge,
weak_factory_.GetWeakPtr(), input,
command_line->GetSwitchValueASCII("label"),
command_line->GetSwitchValueASCII("user"));
}
} else if (args.front() == kGetEnrollmentId) {
task = base::BindOnce(&ClientLoop::GetEnrollmentId,
weak_factory_.GetWeakPtr(),
command_line->HasSwitch("ignore_cache"));
} else if (args.front() == kGetCertifiedNvIndex) {
task = base::BindOnce(&ClientLoop::GetCertifiedNvIndex,
weak_factory_.GetWeakPtr(),
command_line->GetSwitchValueASCII("index"),
command_line->GetSwitchValueASCII("size"),
command_line->GetSwitchValueASCII("key_label"));
} else {
return EX_USAGE;
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(task));
return EX_OK;
}
int GetVerifiedAccessServerType(base::CommandLine* command_line,
VAType* va_type) {
*va_type = DEFAULT_VA;
if (command_line->HasSwitch("va-server")) {
std::string va_server(command_line->GetSwitchValueASCII("va-server"));
if (va_server == "test") {
*va_type = TEST_VA;
} else if (va_server != "" && va_server != "default") {
LOG(ERROR) << "Invalid va-server value: " << va_server;
return EX_USAGE;
}
} else {
// Convert the CA type to a VA server type.
ACAType aca_type;
int status = GetCertificateAuthorityServerType(command_line, &aca_type);
if (status != EX_OK) {
return status;
}
switch (aca_type) {
case TEST_ACA:
*va_type = TEST_VA;
break;
case DEFAULT_ACA:
default:
*va_type = DEFAULT_VA;
break;
}
}
return EX_OK;
}
int GetCertificateAuthorityServerType(base::CommandLine* command_line,
ACAType* aca_type) {
*aca_type = DEFAULT_ACA;
std::string aca_server(
command_line->GetSwitchValueASCII("attestation-server"));
if (aca_server == "test") {
*aca_type = TEST_ACA;
} else if (aca_server != "" && aca_server != "default") {
LOG(ERROR) << "Invalid attestation-server value: " << aca_server;
return EX_USAGE;
}
return EX_OK;
}
int GetKeyType(base::CommandLine* command_line, KeyType* key_type) {
*key_type = KEY_TYPE_RSA;
std::string key_type_str = command_line->GetSwitchValueASCII("key-type");
if (key_type_str == "ecc") {
*key_type = KEY_TYPE_ECC;
} else if (key_type_str != "" && key_type_str != "rsa") {
LOG(ERROR) << "Invalid key-type value: " << key_type_str;
return EX_USAGE;
}
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 PrintErrorAndQuit(brillo::Error* error) {
printf("Error: %s\n", error->GetMessage().c_str());
Quit();
}
void CallGetStatus(bool extended_status) {
GetStatusRequest request;
request.set_extended_status(extended_status);
attestation_->GetStatusAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<GetStatusReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallGetKeyInfo(const std::string& label, const std::string& username) {
GetKeyInfoRequest request;
request.set_key_label(label);
request.set_username(username);
attestation_->GetKeyInfoAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<GetKeyInfoReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallSetKeyPayload(const std::string& payload,
const std::string& label,
const std::string& username) {
SetKeyPayloadRequest request;
request.set_key_label(label);
request.set_username(username);
request.set_payload(payload);
attestation_->SetKeyPayloadAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<SetKeyPayloadReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallDeleteKeys(const std::string& prefix,
const std::string& label,
const std::string& username) {
DeleteKeysRequest request;
if (!label.empty()) {
request.set_key_label_match(label);
request.set_match_behavior(DeleteKeysRequest::MATCH_BEHAVIOR_EXACT);
}
if (!prefix.empty()) {
request.set_key_label_match(prefix);
request.set_match_behavior(DeleteKeysRequest::MATCH_BEHAVIOR_PREFIX);
}
request.set_username(username);
attestation_->DeleteKeysAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<DeleteKeysReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallGetEndorsementInfo() {
GetEndorsementInfoRequest request;
attestation_->GetEndorsementInfoAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<GetEndorsementInfoReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallGetAttestationKeyInfo(ACAType aca_type) {
GetAttestationKeyInfoRequest request;
request.set_aca_type(aca_type);
attestation_->GetAttestationKeyInfoAsync(
request,
base::BindOnce(
&ClientLoop::PrintReplyAndQuit<GetAttestationKeyInfoReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallVerifyAttestation(bool cros_core, bool ek_only) {
VerifyRequest request;
request.set_cros_core(cros_core);
request.set_ek_only(ek_only);
attestation_->VerifyAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<VerifyReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallActivateAttestationKey(ACAType aca_type,
const std::string& input,
bool save_certificate) {
ActivateAttestationKeyRequest request;
request.set_aca_type(aca_type);
request.mutable_encrypted_certificate()->ParseFromString(input);
request.set_save_certificate(save_certificate);
attestation_->ActivateAttestationKeyAsync(
request,
base::BindOnce(
&ClientLoop::PrintReplyAndQuit<ActivateAttestationKeyReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void EncryptForActivate(const std::string& input) {
GetEndorsementInfoRequest request;
attestation_->GetEndorsementInfoAsync(
request,
base::BindOnce(&ClientLoop::EncryptForActivate2,
weak_factory_.GetWeakPtr(), input),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void EncryptForActivate2(const std::string& input,
const GetEndorsementInfoReply& endorsement_info) {
if (endorsement_info.status() != STATUS_SUCCESS) {
PrintReplyAndQuit(endorsement_info);
}
GetAttestationKeyInfoRequest request;
attestation_->GetAttestationKeyInfoAsync(
request,
base::BindOnce(&ClientLoop::EncryptForActivate3,
weak_factory_.GetWeakPtr(), input, endorsement_info),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
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);
}
hwsec::FactoryImpl factory(hwsec::ThreadingMode::kCurrentThread);
auto hwsec = factory.GetAttestationFrontend();
CryptoUtilityImpl crypto(hwsec.get());
EncryptedIdentityCredential encrypted;
TpmVersion tpm_version;
TPM_SELECT_BEGIN;
TPM1_SECTION({ tpm_version = TPM_1_2; });
TPM2_SECTION({ tpm_version = TPM_2_0; });
OTHER_TPM_SECTION({
LOG(ERROR) << "Calling on none supported TPM platform.";
tpm_version = TPM_2_0;
});
TPM_SELECT_END;
if (!crypto.EncryptIdentityCredential(
tpm_version, 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 CallGetFeatures() {
GetFeaturesRequest request;
attestation_->GetFeaturesAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<GetFeaturesReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
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_->CreateCertifiableKeyAsync(
request,
base::BindOnce(
&ClientLoop::PrintReplyAndQuit<CreateCertifiableKeyReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
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_->GetKeyInfoAsync(
request,
base::BindOnce(&ClientLoop::Encrypt2, weak_factory_.GetWeakPtr(),
input),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void Encrypt2(const std::string& input, const GetKeyInfoReply& key_info) {
hwsec::FactoryImpl factory(hwsec::ThreadingMode::kCurrentThread);
auto hwsec = factory.GetAttestationFrontend();
CryptoUtilityImpl crypto(hwsec.get());
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_->DecryptAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<DecryptReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
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_->SignAsync(
request,
base::BindOnce(&ClientLoop::OnSignComplete, weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
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_->GetKeyInfoAsync(
request,
base::BindOnce(&ClientLoop::VerifySignature2,
weak_factory_.GetWeakPtr(), input, signature),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void VerifySignature2(const std::string& input,
const std::string& signature,
const GetKeyInfoReply& key_info) {
hwsec::FactoryImpl factory(hwsec::ThreadingMode::kCurrentThread);
auto hwsec = factory.GetAttestationFrontend();
CryptoUtilityImpl crypto(hwsec.get());
if (crypto.VerifySignature(crypto.DefaultDigestAlgoForSignature(),
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_->RegisterKeyWithChapsTokenAsync(
request,
base::BindOnce(
&ClientLoop::PrintReplyAndQuit<RegisterKeyWithChapsTokenReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallCreateEnrollRequest(ACAType aca_type) {
CreateEnrollRequestRequest request;
request.set_aca_type(aca_type);
attestation_->CreateEnrollRequestAsync(
request,
base::BindOnce(&ClientLoop::OnCreateEnrollRequestComplete,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void OnCreateEnrollRequestComplete(const CreateEnrollRequestReply& reply) {
if (reply.status() == STATUS_SUCCESS &&
base::CommandLine::ForCurrentProcess()->HasSwitch("output")) {
WriteOutput(reply.pca_request());
}
PrintReplyAndQuit<CreateEnrollRequestReply>(reply);
}
void CallFinishEnroll(ACAType aca_type, const std::string& pca_response) {
FinishEnrollRequest request;
request.set_aca_type(aca_type);
request.set_pca_response(pca_response);
attestation_->FinishEnrollAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<FinishEnrollReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallEnroll(ACAType aca_type, bool forced) {
EnrollRequest request;
request.set_aca_type(aca_type);
request.set_forced(forced);
attestation_->EnrollAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<EnrollReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallCreateCertRequest(
ACAType aca_type,
CertificateProfile profile,
const std::string& username,
const std::string& origin,
KeyType key_type,
const std::optional<CertProfileSpecificData>& profile_specific_data) {
CreateCertificateRequestRequest request;
request.set_aca_type(aca_type);
request.set_certificate_profile(profile);
request.set_username(username);
request.set_request_origin(origin);
request.set_key_type(key_type);
if (profile == DEVICE_SETUP_CERTIFICATE) {
if (profile_specific_data &&
std::holds_alternative<DeviceSetupCertificateRequestMetadata>(
profile_specific_data.value())) {
const DeviceSetupCertificateRequestMetadata& metadata =
std::get<DeviceSetupCertificateRequestMetadata>(
profile_specific_data.value());
request.mutable_device_setup_certificate_request_metadata()->set_id(
metadata.id());
request.mutable_device_setup_certificate_request_metadata()
->set_content_binding(metadata.content_binding());
}
}
attestation_->CreateCertificateRequestAsync(
request,
base::BindOnce(&ClientLoop::OnCreateCertRequestComplete,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void OnCreateCertRequestComplete(const CreateCertificateRequestReply& reply) {
if (reply.status() == STATUS_SUCCESS &&
base::CommandLine::ForCurrentProcess()->HasSwitch("output")) {
WriteOutput(reply.pca_request());
}
PrintReplyAndQuit<CreateCertificateRequestReply>(reply);
}
void CallFinishCertRequest(const std::string& pca_response,
const std::string& label,
const std::string& username) {
FinishCertificateRequestRequest request;
request.set_pca_response(pca_response);
request.set_key_label(label);
request.set_username(username);
attestation_->FinishCertificateRequestAsync(
request,
base::BindOnce(
&ClientLoop::PrintReplyAndQuit<FinishCertificateRequestReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallGetCert(
ACAType aca_type,
CertificateProfile profile,
const std::string& label,
const std::string& username,
const std::string& origin,
KeyType key_type,
bool forced,
bool shall_trigger_enrollment,
const std::optional<CertProfileSpecificData>& profile_specific_data) {
GetCertificateRequest request;
request.set_aca_type(aca_type);
request.set_certificate_profile(profile);
request.set_key_label(label);
request.set_username(username);
request.set_request_origin(origin);
request.set_key_type(key_type);
request.set_forced(forced);
request.set_shall_trigger_enrollment(shall_trigger_enrollment);
if (profile == DEVICE_SETUP_CERTIFICATE) {
if (profile_specific_data &&
std::holds_alternative<DeviceSetupCertificateRequestMetadata>(
profile_specific_data.value())) {
const DeviceSetupCertificateRequestMetadata& metadata =
std::get<DeviceSetupCertificateRequestMetadata>(
profile_specific_data.value());
request.mutable_device_setup_certificate_request_metadata()->set_id(
metadata.id());
request.mutable_device_setup_certificate_request_metadata()
->set_content_binding(metadata.content_binding());
}
}
attestation_->GetCertificateAsync(
request,
base::BindOnce(&ClientLoop::PrintReplyAndQuit<GetCertificateReply>,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void CallSignEnterpriseChallenge(VAType va_type,
const std::string& input,
const std::string& label,
const std::string& username,
const std::string& domain,
const std::string& device_id,
bool include_spkac) {
SignEnterpriseChallengeRequest request;
request.set_va_type(va_type);
request.set_key_label(label);
request.set_username(username);
request.set_domain(domain);
request.set_device_id(device_id);
request.set_include_signed_public_key(include_spkac);
request.set_challenge(input);
attestation_->SignEnterpriseChallengeAsync(
request,
base::BindOnce(&ClientLoop::OnSignEnterpriseChallengeComplete,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void OnSignEnterpriseChallengeComplete(
const SignEnterpriseChallengeReply& reply) {
if (reply.status() == STATUS_SUCCESS &&
base::CommandLine::ForCurrentProcess()->HasSwitch("output")) {
WriteOutput(reply.challenge_response());
}
PrintReplyAndQuit<SignEnterpriseChallengeReply>(reply);
}
void CallSignSimpleChallenge(const std::string& input,
const std::string& label,
const std::string& username) {
SignSimpleChallengeRequest request;
request.set_key_label(label);
request.set_username(username);
request.set_challenge(input);
attestation_->SignSimpleChallengeAsync(
request,
base::BindOnce(&ClientLoop::OnSignSimpleChallengeComplete,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void OnSignSimpleChallengeComplete(const SignSimpleChallengeReply& reply) {
if (reply.status() == STATUS_SUCCESS &&
base::CommandLine::ForCurrentProcess()->HasSwitch("output")) {
WriteOutput(reply.challenge_response());
}
PrintReplyAndQuit<SignSimpleChallengeReply>(reply);
}
void GetEnrollmentId(bool ignore_cache) {
GetEnrollmentIdRequest request;
request.set_ignore_cache(ignore_cache);
attestation_->GetEnrollmentIdAsync(
request,
base::BindOnce(&ClientLoop::OnGetEnrollmentIdComplete,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void OnGetEnrollmentIdComplete(const GetEnrollmentIdReply& reply) {
PrintReplyAndQuit<GetEnrollmentIdReply>(reply);
}
void GetCertifiedNvIndex(const std::string& index,
const std::string& size_bytes,
const std::string& key_label) {
GetCertifiedNvIndexRequest request;
uint32_t parsed_index;
uint32_t parsed_size;
if (!base::HexStringToUInt(index, &parsed_index))
LOG(ERROR) << "Failed to parse index.";
if (!base::StringToUint(size_bytes, &parsed_size))
LOG(ERROR) << "Failed to parse size.";
request.set_nv_index(parsed_index);
request.set_nv_size(parsed_size);
request.set_key_label(key_label);
attestation_->GetCertifiedNvIndexAsync(
request,
base::BindOnce(&ClientLoop::OnGetCertifiedNvIndexComplete,
weak_factory_.GetWeakPtr()),
base::BindOnce(&ClientLoop::PrintErrorAndQuit,
weak_factory_.GetWeakPtr()),
kDefaultTimeout.InMilliseconds());
}
void OnGetCertifiedNvIndexComplete(const GetCertifiedNvIndexReply& reply) {
if (reply.status() == STATUS_SUCCESS &&
base::CommandLine::ForCurrentProcess()->HasSwitch("output")) {
WriteOutput(reply.SerializeAsString());
}
PrintReplyAndQuit<GetCertifiedNvIndexReply>(reply);
}
brillo::DBusConnection connection_;
std::unique_ptr<org::chromium::AttestationProxy> attestation_;
// Declare this last so weak pointers will be destroyed first.
base::WeakPtrFactory<ClientLoop> weak_factory_{this};
};
} // namespace attestation
int main(int argc, char* argv[]) {
base::CommandLine::Init(argc, argv);
brillo::InitLog(brillo::kLogToStderr);
attestation::ClientLoop loop;
return loop.Run();
}