blob: 9a6b70c28a2bd645d9b8b4a865c2defab5922ed9 [file] [log] [blame]
// Copyright 2017 Google Inc.
//
// 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 "tink/keyset_handle.h"
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/log/check.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "tink/aead.h"
#include "tink/insecure_secret_key_access.h"
#include "tink/internal/key_gen_configuration_impl.h"
#include "tink/internal/key_info.h"
#include "tink/internal/key_status_util.h"
#include "tink/internal/mutable_serialization_registry.h"
#include "tink/internal/proto_key_serialization.h"
#include "tink/internal/util.h"
#include "tink/key_gen_configuration.h"
#include "tink/key_status.h"
#include "tink/keyset_reader.h"
#include "tink/keyset_writer.h"
#include "tink/registry.h"
#include "tink/util/errors.h"
#include "tink/util/keyset_util.h"
#include "proto/tink.pb.h"
using google::crypto::tink::EncryptedKeyset;
using google::crypto::tink::KeyData;
using google::crypto::tink::Keyset;
using google::crypto::tink::KeysetInfo;
using google::crypto::tink::KeyStatusType;
using google::crypto::tink::KeyTemplate;
using google::crypto::tink::OutputPrefixType;
namespace crypto {
namespace tink {
namespace {
util::StatusOr<std::unique_ptr<EncryptedKeyset>> Encrypt(
const Keyset& keyset, const Aead& master_key_aead,
absl::string_view associated_data) {
auto encrypt_result =
master_key_aead.Encrypt(keyset.SerializeAsString(), associated_data);
if (!encrypt_result.ok()) return encrypt_result.status();
auto enc_keyset = absl::make_unique<EncryptedKeyset>();
enc_keyset->set_encrypted_keyset(encrypt_result.value());
return std::move(enc_keyset);
}
util::StatusOr<std::unique_ptr<Keyset>> Decrypt(
const EncryptedKeyset& enc_keyset, const Aead& master_key_aead,
absl::string_view associated_data) {
auto decrypt_result =
master_key_aead.Decrypt(enc_keyset.encrypted_keyset(), associated_data);
if (!decrypt_result.ok()) return decrypt_result.status();
auto keyset = absl::make_unique<Keyset>();
if (!keyset->ParseFromString(decrypt_result.value())) {
return util::Status(
absl::StatusCode::kInvalidArgument,
"Could not parse the decrypted data as a Keyset-proto.");
}
return std::move(keyset);
}
util::Status ValidateNoSecret(const Keyset& keyset) {
for (const Keyset::Key& key : keyset.key()) {
if (key.key_data().key_material_type() == KeyData::UNKNOWN_KEYMATERIAL ||
key.key_data().key_material_type() == KeyData::SYMMETRIC ||
key.key_data().key_material_type() == KeyData::ASYMMETRIC_PRIVATE) {
return util::Status(
absl::StatusCode::kFailedPrecondition,
"Cannot create KeysetHandle with secret key material from "
"potentially unencrypted source.");
}
}
return util::OkStatus();
}
util::StatusOr<internal::ProtoKeySerialization> ToProtoKeySerialization(
Keyset::Key key) {
absl::optional<int> id_requirement = absl::nullopt;
if (key.output_prefix_type() != OutputPrefixType::RAW) {
id_requirement = key.key_id();
}
return internal::ProtoKeySerialization::Create(
key.key_data().type_url(),
RestrictedData(key.key_data().value(), InsecureSecretKeyAccess::Get()),
key.key_data().key_material_type(), key.output_prefix_type(),
id_requirement);
}
} // anonymous namespace
util::Status KeysetHandle::ValidateAt(int index) const {
const Keyset::Key& proto_key = get_keyset().key(index);
OutputPrefixType output_prefix_type = proto_key.output_prefix_type();
absl::optional<int> id_requirement = absl::nullopt;
if (output_prefix_type != OutputPrefixType::RAW) {
id_requirement = proto_key.key_id();
}
if (!internal::IsPrintableAscii(proto_key.key_data().type_url())) {
return util::Status(absl::StatusCode::kFailedPrecondition,
"Non-printable ASCII character in type URL.");
}
util::StatusOr<KeyStatus> key_status =
internal::FromKeyStatusType(proto_key.status());
if (!key_status.ok()) return key_status.status();
return util::OkStatus();
}
util::Status KeysetHandle::Validate() const {
int num_primary = 0;
const Keyset& keyset = get_keyset();
for (int i = 0; i < size(); ++i) {
util::Status status = ValidateAt(i);
if (!status.ok()) return status;
Keyset::Key proto_key = keyset.key(i);
if (proto_key.key_id() == keyset.primary_key_id()) {
++num_primary;
if (proto_key.status() != KeyStatusType::ENABLED) {
return util::Status(absl::StatusCode::kFailedPrecondition,
"Keyset has primary that is not enabled");
}
}
}
if (num_primary < 1) {
return util::Status(absl::StatusCode::kFailedPrecondition,
"Keyset has no primary");
}
if (num_primary > 1) {
return util::Status(absl::StatusCode::kFailedPrecondition,
"Keyset has more than one primary");
}
return util::OkStatus();
}
KeysetHandle::Entry KeysetHandle::GetPrimary() const {
util::Status validation = Validate();
CHECK_OK(validation);
const Keyset& keyset = get_keyset();
for (int i = 0; i < keyset.key_size(); ++i) {
if (keyset.key(i).key_id() == keyset.primary_key_id()) {
return (*this)[i];
}
}
// Since keyset handle was validated, it should have a valid primary key.
internal::LogFatal("Keyset handle should have a valid primary key.");
}
KeysetHandle::Entry KeysetHandle::operator[](int index) const {
CHECK(index >= 0 && index < size())
<< "Invalid index " << index << " for keyset of size " << size();
if (!entries_.empty() && entries_.size() > index) {
return *entries_[index];
}
// Since `entries_` has not been populated, the entry must be created on
// demand from the key proto entry at `index` in `keyset_`. This special
// case will no longer be necessary after `keyset_` has been removed from the
// `KeysetHandle` class.
//
// TODO(b/277792846): Remove after transition to rely solely on
// `KeysetHandle::Entry`.
return CreateEntryAt(index);
}
KeysetHandle::Entry KeysetHandle::CreateEntryAt(int index) const {
CHECK(index >= 0 && index < size())
<< "Invalid index " << index << " for keyset of size " << size();
util::Status validation = ValidateAt(index);
CHECK_OK(validation);
Keyset keyset = get_keyset();
util::StatusOr<Entry> entry =
CreateEntry(keyset.key(index), keyset.primary_key_id());
// Status should be OK since this keyset handle has been validated.
CHECK_OK(entry.status());
return *entry;
}
util::StatusOr<KeysetHandle::Entry> KeysetHandle::CreateEntry(
const Keyset::Key& proto_key, uint32_t primary_key_id) {
util::StatusOr<internal::ProtoKeySerialization> serialization =
ToProtoKeySerialization(proto_key);
if (!serialization.ok()) {
return serialization.status();
}
util::StatusOr<std::shared_ptr<const Key>> key =
internal::MutableSerializationRegistry::GlobalInstance()
.ParseKeyWithLegacyFallback(*serialization,
InsecureSecretKeyAccess::Get());
if (!key.ok()) {
return key.status();
}
util::StatusOr<KeyStatus> key_status =
internal::FromKeyStatusType(proto_key.status());
if (!key_status.ok()) {
return key_status.status();
}
return Entry(*std::move(key), *key_status, proto_key.key_id(),
proto_key.key_id() == primary_key_id);
}
util::StatusOr<std::unique_ptr<KeysetHandle>> KeysetHandle::Read(
std::unique_ptr<KeysetReader> reader, const Aead& master_key_aead,
const absl::flat_hash_map<std::string, std::string>&
monitoring_annotations) {
return ReadWithAssociatedData(std::move(reader), master_key_aead,
/*associated_data=*/"", monitoring_annotations);
}
util::StatusOr<std::unique_ptr<KeysetHandle>>
KeysetHandle::ReadWithAssociatedData(
std::unique_ptr<KeysetReader> reader, const Aead& master_key_aead,
absl::string_view associated_data,
const absl::flat_hash_map<std::string, std::string>&
monitoring_annotations) {
util::StatusOr<std::unique_ptr<EncryptedKeyset>> enc_keyset_result =
reader->ReadEncrypted();
if (!enc_keyset_result.ok()) {
return ToStatusF(absl::StatusCode::kInvalidArgument,
"Error reading encrypted keyset data: %s",
enc_keyset_result.status().message());
}
auto keyset_result =
Decrypt(*enc_keyset_result.value(), master_key_aead, associated_data);
if (!keyset_result.ok()) {
return ToStatusF(absl::StatusCode::kInvalidArgument,
"Error decrypting encrypted keyset: %s",
keyset_result.status().message());
}
util::StatusOr<std::vector<std::shared_ptr<const Entry>>> entries =
GetEntriesFromKeyset(**keyset_result);
if (!entries.ok()) {
return entries.status();
}
if (entries->size() != (*keyset_result)->key_size()) {
return util::Status(absl::StatusCode::kInternal,
"Error converting keyset proto into key entries.");
}
return absl::WrapUnique(new KeysetHandle(*std::move(keyset_result), *entries,
monitoring_annotations));
}
util::StatusOr<std::unique_ptr<KeysetHandle>> KeysetHandle::ReadNoSecret(
const std::string& serialized_keyset,
const absl::flat_hash_map<std::string, std::string>&
monitoring_annotations) {
Keyset keyset;
if (!keyset.ParseFromString(serialized_keyset)) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Could not parse the input string as a Keyset-proto.");
}
util::Status validation = ValidateNoSecret(keyset);
if (!validation.ok()) {
return validation;
}
util::StatusOr<std::vector<std::shared_ptr<const Entry>>> entries =
GetEntriesFromKeyset(keyset);
if (!entries.ok()) {
return entries.status();
}
if (entries->size() != keyset.key_size()) {
return util::Status(absl::StatusCode::kInternal,
"Error converting keyset proto into key entries.");
}
return absl::WrapUnique(
new KeysetHandle(std::move(keyset), *entries, monitoring_annotations));
}
util::Status KeysetHandle::Write(KeysetWriter* writer,
const Aead& master_key_aead) const {
return WriteWithAssociatedData(writer, master_key_aead, "");
}
util::Status KeysetHandle::WriteWithAssociatedData(
KeysetWriter* writer, const Aead& master_key_aead,
absl::string_view associated_data) const {
if (writer == nullptr) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Writer must be non-null");
}
auto encrypt_result = Encrypt(get_keyset(), master_key_aead, associated_data);
if (!encrypt_result.ok()) {
return ToStatusF(absl::StatusCode::kInvalidArgument,
"Encryption of the keyset failed: %s",
encrypt_result.status().message());
}
return writer->Write(*(encrypt_result.value()));
}
util::Status KeysetHandle::WriteNoSecret(KeysetWriter* writer) const {
if (writer == nullptr) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Writer must be non-null");
}
util::Status validation = ValidateNoSecret(get_keyset());
if (!validation.ok()) return validation;
return writer->Write(get_keyset());
}
util::StatusOr<std::unique_ptr<KeysetHandle>> KeysetHandle::GenerateNew(
const KeyTemplate& key_template, const KeyGenConfiguration& config,
const absl::flat_hash_map<std::string, std::string>&
monitoring_annotations) {
auto handle =
absl::WrapUnique(new KeysetHandle(Keyset(), monitoring_annotations));
util::StatusOr<uint32_t> const result =
handle->AddKey(key_template, /*as_primary=*/true, config);
if (!result.ok()) {
return result.status();
}
return std::move(handle);
}
util::StatusOr<std::unique_ptr<KeysetHandle>> KeysetHandle::GenerateNew(
const KeyTemplate& key_template,
const absl::flat_hash_map<std::string, std::string>&
monitoring_annotations) {
KeyGenConfiguration config;
util::Status status =
internal::KeyGenConfigurationImpl::SetGlobalRegistryMode(config);
if (!status.ok()) {
return status;
}
return GenerateNew(key_template, config, monitoring_annotations);
}
util::StatusOr<std::unique_ptr<Keyset::Key>> ExtractPublicKey(
const Keyset::Key& key) {
if (key.key_data().key_material_type() != KeyData::ASYMMETRIC_PRIVATE) {
return util::Status(
absl::StatusCode::kInvalidArgument,
"Key material is not of type KeyData::ASYMMETRIC_PRIVATE");
}
auto key_data_result = Registry::GetPublicKeyData(key.key_data().type_url(),
key.key_data().value());
if (!key_data_result.ok()) return key_data_result.status();
auto public_key = absl::make_unique<Keyset::Key>(key);
public_key->mutable_key_data()->Swap(key_data_result.value().get());
return std::move(public_key);
}
util::StatusOr<std::unique_ptr<KeysetHandle>>
KeysetHandle::GetPublicKeysetHandle() const {
std::unique_ptr<Keyset> public_keyset(new Keyset());
for (const Keyset::Key& key : get_keyset().key()) {
auto public_key_result = ExtractPublicKey(key);
if (!public_key_result.ok()) return public_key_result.status();
public_keyset->add_key()->Swap(public_key_result.value().get());
}
public_keyset->set_primary_key_id(get_keyset().primary_key_id());
util::StatusOr<std::vector<std::shared_ptr<const Entry>>> entries =
GetEntriesFromKeyset(*public_keyset);
if (!entries.ok()) {
return entries.status();
}
if (entries->size() != public_keyset->key_size()) {
return util::Status(absl::StatusCode::kInternal,
"Error converting keyset proto into key entries.");
}
std::unique_ptr<KeysetHandle> handle(
new KeysetHandle(std::move(public_keyset), *entries));
return std::move(handle);
}
crypto::tink::util::StatusOr<uint32_t> KeysetHandle::AddToKeyset(
const google::crypto::tink::KeyTemplate& key_template, bool as_primary,
const KeyGenConfiguration& config, Keyset* keyset) {
if (key_template.output_prefix_type() ==
google::crypto::tink::OutputPrefixType::UNKNOWN_PREFIX) {
return util::Status(absl::StatusCode::kInvalidArgument,
"key template has unknown prefix");
}
// Generate new key data.
util::StatusOr<std::unique_ptr<KeyData>> key_data;
if (internal::KeyGenConfigurationImpl::IsInGlobalRegistryMode(config)) {
key_data = Registry::NewKeyData(key_template);
} else {
util::StatusOr<const internal::KeyTypeInfoStore*> key_type_info_store =
internal::KeyGenConfigurationImpl::GetKeyTypeInfoStore(config);
if (!key_type_info_store.ok()) {
return key_type_info_store.status();
}
util::StatusOr<const internal::KeyTypeInfoStore::Info*> key_type_info =
(*key_type_info_store)->Get(key_template.type_url());
if (!key_type_info.ok()) {
return key_type_info.status();
}
key_data = (*key_type_info)->key_factory().NewKeyData(key_template.value());
}
if (!key_data.ok()) {
return key_data.status();
}
// Add and fill in new key in `keyset`.
Keyset::Key* key = keyset->add_key();
*(key->mutable_key_data()) = *std::move(key_data).value();
key->set_status(KeyStatusType::ENABLED);
key->set_output_prefix_type(key_template.output_prefix_type());
uint32_t key_id = GenerateUnusedKeyId(*keyset);
key->set_key_id(key_id);
if (as_primary) {
keyset->set_primary_key_id(key_id);
}
return key_id;
}
crypto::tink::util::StatusOr<uint32_t> KeysetHandle::AddKey(
const google::crypto::tink::KeyTemplate& key_template, bool as_primary,
const KeyGenConfiguration& config) {
util::StatusOr<uint32_t> id =
AddToKeyset(key_template, as_primary, config, &keyset_);
if (!id.ok()) {
return id.status();
}
util::StatusOr<const Entry> entry = CreateEntry(
keyset_.key(keyset_.key_size() - 1), keyset_.primary_key_id());
if (!entry.ok()) {
return entry.status();
}
entries_.push_back(std::make_shared<const Entry>(*entry));
return *id;
}
KeysetInfo KeysetHandle::GetKeysetInfo() const {
return KeysetInfoFromKeyset(get_keyset());
}
util::StatusOr<std::vector<std::shared_ptr<const KeysetHandle::Entry>>>
KeysetHandle::GetEntriesFromKeyset(const Keyset& keyset) {
std::vector<std::shared_ptr<const Entry>> entries;
for (const Keyset::Key& key : keyset.key()) {
util::StatusOr<const Entry> entry =
CreateEntry(key, keyset.primary_key_id());
if (!entry.ok()) {
return entry.status();
}
entries.push_back(std::make_shared<const Entry>(*entry));
}
return entries;
}
} // namespace tink
} // namespace crypto