blob: 19cbb5ac88e4fa478ce1675bc7095c7560678cc3 [file] [log] [blame]
// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "easy-unlock/dbus_adaptor.h"
#include <stdint.h>
#include <vector>
#include <base/bind.h>
#include <base/callback.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/exported_object.h>
#include <dbus/message.h>
#include "easy-unlock/easy_unlock_service.h"
namespace easy_unlock {
namespace {
const char kBindingsPath[] =
"/usr/share/dbus-1/interfaces/org.chromium.EasyUnlockInterface.xml";
const char kDBusIntrospectableInterface[] =
"org.freedesktop.DBus.Introspectable";
const char kDBusIntrospectMethod[] = "Introspect";
// Utility method for extracting byte vector arguments from DBus method calls.
// |reader|: MessageReader for the method call.
// |bytes|: Byte vector extracted from message reader.
// Returns whether the bytes were successfully extracted.
bool ReadArrayOfBytes(dbus::MessageReader* reader,
std::vector<uint8_t>* bytes) {
DCHECK(bytes);
DCHECK(reader);
const uint8_t* raw_bytes;
size_t raw_bytes_size;
if (!reader->PopArrayOfBytes(&raw_bytes, &raw_bytes_size))
return false;
bytes->assign(raw_bytes, raw_bytes + raw_bytes_size);
return true;
}
// Reads encryption type string passed in DBus method call and converts it to
// ServiceImpl::EncryptionType enum. Returns whether the parameter
// was successfully read and converted.
bool ReadAndConvertEncryptionType(
dbus::MessageReader* reader,
easy_unlock_crypto::ServiceImpl::EncryptionType* type) {
std::string encryption_type_str;
if (!reader->PopString(&encryption_type_str))
return false;
if (encryption_type_str == kEncryptionTypeNone) {
*type = easy_unlock_crypto::ServiceImpl::ENCRYPTION_TYPE_NONE;
return true;
}
if (encryption_type_str == kEncryptionTypeAES256CBC) {
*type = easy_unlock_crypto::ServiceImpl::ENCRYPTION_TYPE_AES_256_CBC;
return true;
}
return false;
}
// Reads signature type string passed in DBus method call and converts it to
// ServiceImpl::SignatureType enum. Returns whether the parameter
// was successfully read and converted.
bool ReadAndConvertSignatureType(
dbus::MessageReader* reader,
easy_unlock_crypto::ServiceImpl::SignatureType* type) {
std::string signature_type_str;
if (!reader->PopString(&signature_type_str))
return false;
if (signature_type_str == kSignatureTypeECDSAP256SHA256) {
*type = easy_unlock_crypto::ServiceImpl::SIGNATURE_TYPE_ECDSA_P256_SHA256;
return true;
}
if (signature_type_str == kSignatureTypeHMACSHA256) {
*type = easy_unlock_crypto::ServiceImpl::SIGNATURE_TYPE_HMAC_SHA256;
return true;
}
return false;
}
// Reads public key algorithm string passed to DBus method call and converts
// it to ServiceImpl::KeyAlgorithm enum. Returns whether the parameter
// was successfully read and converted.
bool ReadAndConvertKeyAlgorithm(
dbus::MessageReader* reader,
easy_unlock_crypto::ServiceImpl::KeyAlgorithm* algorithm) {
std::string key_algorithm_str;
if (!reader->PopString(&key_algorithm_str))
return false;
if (key_algorithm_str == kKeyAlgorithmRSA) {
*algorithm = easy_unlock_crypto::ServiceImpl::KEY_ALGORITHM_RSA;
return true;
}
if (key_algorithm_str == kKeyAlgorithmECDSA) {
*algorithm = easy_unlock_crypto::ServiceImpl::KEY_ALGORITHM_ECDSA;
return true;
}
return false;
}
// Utility method for running handlers for DBus method calls.
void HandleSynchronousDBusMethodCall(
const base::Callback<
scoped_ptr<dbus::Response>(dbus::MethodCall*)>& handler,
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
scoped_ptr<dbus::Response> response = handler.Run(method_call);
if (!response)
response = dbus::Response::FromMethodCall(method_call);
response_sender.Run(response.Pass());
}
} // namespace
DBusAdaptor::DBusAdaptor(easy_unlock::Service* service)
: service_impl_(service) {
CHECK(service_impl_) << "Service implementation not passed to DBus adaptor";
}
DBusAdaptor::~DBusAdaptor() {}
void DBusAdaptor::ExportDBusMethods(dbus::ExportedObject* object) {
ExportSyncDBusMethod(object, kGenerateEcP256KeyPairMethod,
&DBusAdaptor::GenerateEcP256KeyPair);
ExportSyncDBusMethod(object, kWrapPublicKeyMethod,
&DBusAdaptor::WrapPublicKey);
ExportSyncDBusMethod(object, kPerformECDHKeyAgreementMethod,
&DBusAdaptor::PerformECDHKeyAgreement);
ExportSyncDBusMethod(object, kCreateSecureMessageMethod,
&DBusAdaptor::CreateSecureMessage);
ExportSyncDBusMethod(object, kUnwrapSecureMessageMethod,
&DBusAdaptor::UnwrapSecureMessage);
CHECK(object->ExportMethodAndBlock(
kDBusIntrospectableInterface,
kDBusIntrospectMethod,
base::Bind(&HandleSynchronousDBusMethodCall,
base::Bind(&DBusAdaptor::Introspect,
base::Unretained(this)))));
}
scoped_ptr<dbus::Response> DBusAdaptor::Introspect(dbus::MethodCall* call) {
std::string output;
if (!base::ReadFileToString(base::FilePath(kBindingsPath), &output)) {
PLOG(ERROR) << "Cannot read XML bindings from disk";
return dbus::ErrorResponse::FromMethodCall(
call, "Cannot read XML bindings from disk.", "").Pass();
}
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
writer.AppendString(output);
return response.Pass();
}
scoped_ptr<dbus::Response> DBusAdaptor::GenerateEcP256KeyPair(
dbus::MethodCall* method_call) {
std::vector<uint8_t> private_key;
std::vector<uint8_t> public_key;
service_impl_->GenerateEcP256KeyPair(&private_key, &public_key);
scoped_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
writer.AppendArrayOfBytes(private_key.data(), private_key.size());
writer.AppendArrayOfBytes(public_key.data(), public_key.size());
return response.Pass();
}
scoped_ptr<dbus::Response> DBusAdaptor::WrapPublicKey(
dbus::MethodCall* method_call) {
dbus::MessageReader reader(method_call);
easy_unlock_crypto::ServiceImpl::KeyAlgorithm algorithm;
std::vector<uint8_t> public_key;
std::vector<uint8_t> wrapped_key;
if (ReadAndConvertKeyAlgorithm(&reader, &algorithm) &&
ReadArrayOfBytes(&reader, &public_key)) {
wrapped_key = service_impl_->WrapPublicKey(algorithm, public_key);
}
scoped_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
writer.AppendArrayOfBytes(wrapped_key.data(), wrapped_key.size());
return response.Pass();
}
scoped_ptr<dbus::Response> DBusAdaptor::PerformECDHKeyAgreement(
dbus::MethodCall* method_call) {
dbus::MessageReader reader(method_call);
std::vector<uint8_t> private_key;
std::vector<uint8_t> public_key;
std::vector<uint8_t> secret_key;
if (ReadArrayOfBytes(&reader, &private_key) &&
ReadArrayOfBytes(&reader, &public_key)) {
secret_key =
service_impl_->PerformECDHKeyAgreement(private_key, public_key);
} else {
LOG(ERROR) << "Invalid arguments for PerformECDHKeyAgreement method";
}
scoped_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
writer.AppendArrayOfBytes(secret_key.data(), secret_key.size());
return response.Pass();
}
scoped_ptr<dbus::Response> DBusAdaptor::CreateSecureMessage(
dbus::MethodCall* method_call) {
dbus::MessageReader reader(method_call);
std::vector<uint8_t> payload;
std::vector<uint8_t> key;
std::vector<uint8_t> associated_data;
std::vector<uint8_t> public_metadata;
std::vector<uint8_t> verification_key_id;
std::vector<uint8_t> decryption_key_id;
easy_unlock_crypto::ServiceImpl::EncryptionType encryption_type;
easy_unlock_crypto::ServiceImpl::SignatureType signature_type;
std::vector<uint8_t> message;
if (ReadArrayOfBytes(&reader, &payload) &&
ReadArrayOfBytes(&reader, &key) &&
ReadArrayOfBytes(&reader, &associated_data) &&
ReadArrayOfBytes(&reader, &public_metadata) &&
ReadArrayOfBytes(&reader, &verification_key_id) &&
// TODO(tbarzic): Require to be present decryption_key_id when Chrome
// gets updated to a newer version, which supports passing
// decryption_key_id.
(ReadArrayOfBytes(&reader, &decryption_key_id) || true) &&
ReadAndConvertEncryptionType(&reader, &encryption_type) &&
ReadAndConvertSignatureType(&reader, &signature_type)) {
message = service_impl_->CreateSecureMessage(payload,
key,
associated_data,
public_metadata,
verification_key_id,
decryption_key_id,
encryption_type,
signature_type);
} else {
LOG(ERROR) << "Invalid arguments for CreateSecureMessage method";
}
scoped_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
writer.AppendArrayOfBytes(message.data(), message.size());
return response.Pass();
}
scoped_ptr<dbus::Response> DBusAdaptor::UnwrapSecureMessage(
dbus::MethodCall* method_call) {
dbus::MessageReader reader(method_call);
std::vector<uint8_t> message;
std::vector<uint8_t> key;
std::vector<uint8_t> associated_data;
easy_unlock_crypto::ServiceImpl::EncryptionType encryption_type;
easy_unlock_crypto::ServiceImpl::SignatureType signature_type;
std::vector<uint8_t> unwrapped_message;
if (ReadArrayOfBytes(&reader, &message) &&
ReadArrayOfBytes(&reader, &key) &&
ReadArrayOfBytes(&reader, &associated_data) &&
ReadAndConvertEncryptionType(&reader, &encryption_type) &&
ReadAndConvertSignatureType(&reader, &signature_type)) {
unwrapped_message = service_impl_->UnwrapSecureMessage(message,
key,
associated_data,
encryption_type,
signature_type);
} else {
LOG(ERROR) << "Invalid arguments for UnwrapSecureMessage method";
}
scoped_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
writer.AppendArrayOfBytes(unwrapped_message.data(), unwrapped_message.size());
return response.Pass();
}
void DBusAdaptor::ExportSyncDBusMethod(
dbus::ExportedObject* object,
const std::string& method_name,
SyncDBusMethodCallMemberFunction member) {
DCHECK(object);
CHECK(object->ExportMethodAndBlock(
kEasyUnlockServiceInterface, method_name,
base::Bind(&HandleSynchronousDBusMethodCall,
base::Bind(member, base::Unretained(this)))));
}
} // namespace easy_unlock