// 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. // //////////////////////////////////////////////////////////////////////////////// #ifndef TINK_KEYSET_HANDLE_H_ #define TINK_KEYSET_HANDLE_H_ #include #include #include #include #include #include "absl/base/attributes.h" #include "absl/container/flat_hash_map.h" #include "absl/memory/memory.h" #include "absl/status/status.h" #include "tink/aead.h" #include "tink/configuration.h" #include "tink/internal/configuration_impl.h" #include "tink/internal/key_info.h" #include "tink/key.h" #include "tink/key_gen_configuration.h" #include "tink/key_manager.h" #include "tink/key_status.h" #include "tink/keyset_reader.h" #include "tink/keyset_writer.h" #include "tink/primitive_set.h" #include "tink/registry.h" #include "tink/util/statusor.h" #include "proto/tink.pb.h" namespace crypto { namespace tink { // KeysetHandle provides abstracted access to Keysets, to limit // the exposure of actual protocol buffers that hold sensitive // key material. class KeysetHandle { public: // Represents a single entry in a `KeysetHandle`. Some current behavior will // be changed in the future. class Entry { public: // May return an internal class in case there is no implementation of the // corresponding key class yet. Returned value only valid for lifetime // of entry object. std::shared_ptr GetKey() const { return key_; } // Status indicates whether or not a key should still be used. KeyStatus GetStatus() const { return status_; } // ID should be unique (though currently Tink still accepts keysets with // repeated IDs). int GetId() const { return id_; } // Should return true for exactly one entry (though currently Tink still // accepts keysets which have no entry marked as primary). bool IsPrimary() const { return is_primary_; } private: friend class KeysetHandle; friend class KeysetHandleBuilder; Entry(std::shared_ptr key, KeyStatus status, int id, bool is_primary) : key_(std::move(key)), status_(status), id_(id), is_primary_(is_primary) {} std::shared_ptr key_; KeyStatus status_; int id_; bool is_primary_; }; // Returns the number of entries in this keyset. int size() const { return keyset_.key_size(); } // Validates single `KeysetHandle::Entry` at `index` by making sure that the // key entry's type URL is printable and that it has a valid key status. crypto::tink::util::Status ValidateAt(int index) const; // Validates each individual `KeysetHandle::Entry` in keyset handle by calling // `ValidateAt()`. Also, checks that there is a single enabled primary key. crypto::tink::util::Status Validate() const; // Returns entry for primary key in this keyset. Crashes if `Validate()` // does not return an OK status. Call `Validate()` prior to calling this // method to avoid potentially crashing your program. Entry GetPrimary() const; // Returns the `KeysetHandle::Entry` at `index`. Crashes if // `ValidateAt(index)` does not return an OK status. Call `ValidateAt(index)` // prior to calling this method to avoid potentially crashing your program. Entry operator[](int index) const; // Creates a KeysetHandle from an encrypted keyset obtained via `reader` // using `master_key_aead` to decrypt the keyset, with monitoring annotations // `monitoring_annotations`; by default, `monitoring_annotations` is empty. static crypto::tink::util::StatusOr> Read( std::unique_ptr reader, const Aead& master_key_aead, const absl::flat_hash_map& monitoring_annotations = {}); // Creates a KeysetHandle from an encrypted keyset obtained via `reader` // using `master_key_aead` to decrypt the keyset, expecting `associated_data`. // The keyset is annotated for monitoring with `monitoring_annotations`; by // default, `monitoring_annotations` is empty. static crypto::tink::util::StatusOr> ReadWithAssociatedData(std::unique_ptr reader, const Aead& master_key_aead, absl::string_view associated_data, const absl::flat_hash_map& monitoring_annotations = {}); // Creates a KeysetHandle from a serialized keyset `serialized_keyset` which // contains no secret key material, and annotates it with // `monitoring_annotations` for monitoring; by default, // `monitoring_annotations` is empty. This can be used to load public keysets // or envelope encryption keysets. static crypto::tink::util::StatusOr> ReadNoSecret(const std::string& serialized_keyset, const absl::flat_hash_map& monitoring_annotations = {}); // Returns a KeysetHandle containing a single new key generated according to // `key_template` and using `config`. The keyset is annotated for monitoring // with `monitoring_annotations`, which is empty by default. static crypto::tink::util::StatusOr> GenerateNew(const google::crypto::tink::KeyTemplate& key_template, const crypto::tink::KeyGenConfiguration& config, const absl::flat_hash_map& monitoring_annotations = {}); // TODO(b/265865177): Deprecate. // Returns a KeysetHandle containing a single new key generated according to // `key_template`. The keyset is annotated for monitoring with // `monitoring_annotations`, which is empty by default. static crypto::tink::util::StatusOr> GenerateNew(const google::crypto::tink::KeyTemplate& key_template, const absl::flat_hash_map& monitoring_annotations = {}); // Encrypts the underlying keyset with the provided `master_key_aead` // and writes the resulting EncryptedKeyset to the given `writer`, // which must be non-null. crypto::tink::util::Status Write(KeysetWriter* writer, const Aead& master_key_aead) const; // Encrypts the underlying keyset with the provided `master_key_aead`, using // `associated_data`. and writes the resulting EncryptedKeyset to the given // `writer`, which must be non-null. crypto::tink::util::Status WriteWithAssociatedData( KeysetWriter* writer, const Aead& master_key_aead, absl::string_view associated_data) const; // Returns KeysetInfo, a "safe" Keyset that doesn't contain any actual // key material, thus can be used for logging or monitoring. google::crypto::tink::KeysetInfo GetKeysetInfo() const; // Writes the underlying keyset to `writer` only if the keyset does not // contain any secret key material. // This can be used to persist public keysets or envelope encryption keysets. // Users that need to persist cleartext keysets can use // `CleartextKeysetHandle`. crypto::tink::util::Status WriteNoSecret(KeysetWriter* writer) const; // Returns a new KeysetHandle containing public keys corresponding to the // private keys in this handle. Relies on key type managers stored in `config` // to do so. Returns an error if this handle contains keys that are not // private keys. crypto::tink::util::StatusOr> GetPublicKeysetHandle(const KeyGenConfiguration& config) const; // Returns a new KeysetHandle containing public keys corresponding to the // private keys in this handle. Relies on key type managers stored in the // global registry to do so. Returns an error if this handle contains keys // that are not private keys. crypto::tink::util::StatusOr> GetPublicKeysetHandle() const; // Creates a wrapped primitive using this keyset handle and config, which // stores necessary primitive wrappers and key type managers. template crypto::tink::util::StatusOr> GetPrimitive( const Configuration& config) const; // Creates a wrapped primitive using this keyset handle and the global // registry, which stores necessary primitive wrappers and key type managers. template crypto::tink::util::StatusOr> GetPrimitive() const; // Creates a wrapped primitive corresponding to this keyset. Uses the given // KeyManager, as well as the KeyManager and PrimitiveWrapper objects in the // global registry to create the primitive. The given KeyManager is used for // keys supported by it. For those, the registry is ignored. template ABSL_DEPRECATED("Register the keymanager and use GetPrimitive") crypto::tink::util::StatusOr> GetPrimitive( const KeyManager

* custom_manager) const; private: // The classes below need access to get_keyset(); friend class CleartextKeysetHandle; friend class KeysetManager; // TestKeysetHandle::GetKeyset() provides access to get_keyset(). friend class TestKeysetHandle; // KeysetHandleBuilder::Build() needs access to KeysetHandle(Keyset). friend class KeysetHandleBuilder; // Creates a handle that contains the given keyset. explicit KeysetHandle(google::crypto::tink::Keyset keyset) : keyset_(std::move(keyset)) {} explicit KeysetHandle(std::unique_ptr keyset) : keyset_(std::move(*keyset)) {} // Creates a handle that contains the given `keyset` and `entries`. explicit KeysetHandle( google::crypto::tink::Keyset keyset, const std::vector>& entries) : keyset_(std::move(keyset)), entries_(entries) {} explicit KeysetHandle( std::unique_ptr keyset, const std::vector>& entries) : keyset_(std::move(*keyset)), entries_(entries) {} // Creates a handle that contains the given `keyset` and // `monitoring_annotations`. KeysetHandle(google::crypto::tink::Keyset keyset, const absl::flat_hash_map& monitoring_annotations) : keyset_(std::move(keyset)), monitoring_annotations_(monitoring_annotations) {} KeysetHandle(std::unique_ptr keyset, const absl::flat_hash_map& monitoring_annotations) : keyset_(std::move(*keyset)), monitoring_annotations_(monitoring_annotations) {} // Creates a handle that contains the given `keyset`, `entries`, and // `monitoring_annotations`. KeysetHandle(google::crypto::tink::Keyset keyset, const std::vector>& entries, const absl::flat_hash_map& monitoring_annotations) : keyset_(std::move(keyset)), entries_(entries), monitoring_annotations_(monitoring_annotations) {} KeysetHandle(std::unique_ptr keyset, const std::vector>& entries, const absl::flat_hash_map& monitoring_annotations) : keyset_(std::move(*keyset)), entries_(entries), monitoring_annotations_(monitoring_annotations) {} // Generates a key from `key_template` and adds it `keyset`. static crypto::tink::util::StatusOr AddToKeyset( const google::crypto::tink::KeyTemplate& key_template, bool as_primary, const crypto::tink::KeyGenConfiguration& config, google::crypto::tink::Keyset* keyset); // Creates list of KeysetHandle::Entry entries derived from `keyset` in order. static crypto::tink::util::StatusOr>> GetEntriesFromKeyset(const google::crypto::tink::Keyset& keyset); // Creates KeysetHandle::Entry for `key`, which will be set to primary if // its key id equals `primary_key_id`. static util::StatusOr CreateEntry( const google::crypto::tink::Keyset::Key& key, uint32_t primary_key_id); // Generates a key from `key_template` and adds it to the keyset handle. crypto::tink::util::StatusOr AddKey( const google::crypto::tink::KeyTemplate& key_template, bool as_primary, const crypto::tink::KeyGenConfiguration& config); // Returns keyset held by this handle. const google::crypto::tink::Keyset& get_keyset() const { return keyset_; } // Creates a set of primitives corresponding to the keys with // (status == ENABLED) in the keyset given in 'keyset_handle', // assuming all the corresponding key managers are present (keys // with (status != ENABLED) are skipped). // // The returned set is usually later "wrapped" into a class that // implements the corresponding Primitive-interface. template crypto::tink::util::StatusOr>> GetPrimitives( const KeyManager

* custom_manager) const; // Creates KeysetHandle::Entry from `keyset_` at `index`. Entry CreateEntryAt(int index) const; google::crypto::tink::Keyset keyset_; // If this keyset handle has been created with a constructor that does not // accept an entries argument, then `entries` will be empty and operator[] // will fall back to creating the key entry on demand from `keyset_`. // // If `entries_` is not empty, then it should contain exactly one key entry // for each key proto in `keyset_`. std::vector> entries_; absl::flat_hash_map monitoring_annotations_; }; /////////////////////////////////////////////////////////////////////////////// // Implementation details of templated methods. template crypto::tink::util::StatusOr>> KeysetHandle::GetPrimitives(const KeyManager

* custom_manager) const { crypto::tink::util::Status status = ValidateKeyset(get_keyset()); if (!status.ok()) return status; typename PrimitiveSet

::Builder primitives_builder; primitives_builder.AddAnnotations(monitoring_annotations_); for (const google::crypto::tink::Keyset::Key& key : get_keyset().key()) { if (key.status() == google::crypto::tink::KeyStatusType::ENABLED) { std::unique_ptr

primitive; if (custom_manager != nullptr && custom_manager->DoesSupport(key.key_data().type_url())) { auto primitive_result = custom_manager->GetPrimitive(key.key_data()); if (!primitive_result.ok()) return primitive_result.status(); primitive = std::move(primitive_result.value()); } else { auto primitive_result = Registry::GetPrimitive

(key.key_data()); if (!primitive_result.ok()) return primitive_result.status(); primitive = std::move(primitive_result.value()); } if (key.key_id() == get_keyset().primary_key_id()) { primitives_builder.AddPrimaryPrimitive(std::move(primitive), KeyInfoFromKey(key)); } else { primitives_builder.AddPrimitive(std::move(primitive), KeyInfoFromKey(key)); } } } auto primitives = std::move(primitives_builder).Build(); if (!primitives.ok()) return primitives.status(); return absl::make_unique>(*std::move(primitives)); } template crypto::tink::util::StatusOr> KeysetHandle::GetPrimitive( const Configuration& config) const { if (crypto::tink::internal::ConfigurationImpl::IsInGlobalRegistryMode( config)) { return crypto::tink::internal::RegistryImpl::GlobalInstance().WrapKeyset

( keyset_, monitoring_annotations_); } crypto::tink::util::StatusOr< const crypto::tink::internal::KeysetWrapperStore*> wrapper_store = crypto::tink::internal::ConfigurationImpl::GetKeysetWrapperStore( config); if (!wrapper_store.ok()) { return wrapper_store.status(); } crypto::tink::util::StatusOr*> wrapper = (*wrapper_store)->Get

(); if (!wrapper.ok()) { return wrapper.status(); } return (*wrapper)->Wrap(keyset_, monitoring_annotations_); } // TODO(b/265865177): Deprecate. template crypto::tink::util::StatusOr> KeysetHandle::GetPrimitive() const { // TODO(b/265705174): Replace with ConfigGlobalRegistry instance. crypto::tink::Configuration config; crypto::tink::util::Status status = crypto::tink::internal::ConfigurationImpl::SetGlobalRegistryMode(config); if (!status.ok()) { return status; } return GetPrimitive

(config); } template crypto::tink::util::StatusOr> KeysetHandle::GetPrimitive( const KeyManager

* custom_manager) const { if (custom_manager == nullptr) { return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument, "custom_manager must not be null"); } auto primitives_result = this->GetPrimitives

(custom_manager); if (!primitives_result.ok()) { return primitives_result.status(); } return Registry::Wrap

(std::move(primitives_result.value())); } } // namespace tink } // namespace crypto #endif // TINK_KEYSET_HANDLE_H_