diff --git a/cc/BUILD b/cc/BUILD index 74dd7ed8439a7a2ce389e3a110d43f60a21b86ef..f08f31490201fda1016bf0a88cfda813ec030267 100644 --- a/cc/BUILD +++ b/cc/BUILD @@ -1,33 +1,130 @@ licenses(["notice"]) # Apache 2.0 +# TODO(przydatek): consider adding string_view/string_piece to util +# instead of relying on stringpiece in protobuf. cc_library( name = "aead", - hdrs = ["public/aead.h"], + hdrs = ["aead.h"], deps = [ + "//cc/util:statusor", "@com_github_google_protobuf//:protobuf_lite", ], ) +cc_library( + name = "mac", + hdrs = ["mac.h"], + deps = [ + "//cc/util:status", + "//cc/util:statusor", + "@com_github_google_protobuf//:protobuf_lite", + ], +) + +cc_library( + name = "registry", + srcs = ["registry.h"], + hdrs = ["registry.h"], + deps = [ + ":key_manager", + "//cc/util:errors", + "//cc/util:status", + "//cc/util:statusor", + "//proto:cc_core", + "@com_github_google_protobuf//:protobuf_lite", + ], +) + +cc_library( + name = "keyset_handle", + srcs = ["core/keyset_handle.cc"], + hdrs = ["keyset_handle.h"], + deps = [ + "//proto:cc_core", + ], +) + +cc_library( + name = "key_manager", + srcs = ["key_manager.h"], + hdrs = ["key_manager.h"], + deps = [ + "//cc/util:errors", + "//cc/util:status", + "//cc/util:statusor", + "@com_github_google_protobuf//:protobuf_lite", + ], +) + +cc_library( + name = "aead_factory", + srcs = ["core/aead_factory.cc"], + hdrs = ["aead_factory.h"], + deps = [ + ":aead", + ":key_manager", + ":keyset_handle", + "//cc/util:errors", + "//cc/util:status", + "//cc/util:statusor", + ], +) + +cc_library( + name = "mac_factory", + srcs = ["core/mac_factory.cc"], + hdrs = ["mac_factory.h"], + deps = [ + ":key_manager", + ":keyset_handle", + ":mac", + "//cc/util:status", + "//cc/util:statusor", + ], +) + # tests cc_test( - name = "tink_proto_test", + name = "aead_factory_test", size = "small", - srcs = ["tink_proto_test.cc"], + srcs = ["core/aead_factory_test.cc"], copts = ["-Iexternal/gtest/include"], deps = [ - "//proto:cc_core", + ":aead", + ":aead_factory", + "//cc/util:status", + "//cc/util:statusor", + "@gtest//:gtest", + ], +) + +cc_test( + name = "mac_factory_test", + size = "small", + srcs = ["core/mac_factory_test.cc"], + copts = ["-Iexternal/gtest/include"], + deps = [ + ":mac", + ":mac_factory", + "//cc/util:status", + "//cc/util:statusor", "@gtest//:gtest", ], ) cc_test( - name = "ecdsa_proto_test", + name = "registry_test", size = "small", - srcs = ["ecdsa_proto_test.cc"], + srcs = ["core/registry_test.cc"], copts = ["-Iexternal/gtest/include"], deps = [ - "//proto:cc_ecdsa", + ":aead", + ":registry", + "//cc/util:status", + "//cc/util:statusor", + "//proto:cc_aes_ctr_hmac_aead", + "//proto:cc_aes_gcm", "@gtest//:gtest", ], ) diff --git a/cc/aead.h b/cc/aead.h new file mode 100644 index 0000000000000000000000000000000000000000..eeb329b7b1c8d123aac34967dd1041b59f087cef --- /dev/null +++ b/cc/aead.h @@ -0,0 +1,61 @@ +// 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_AEAD_H_ +#define TINK_AEAD_H_ + +#include "cc/util/statusor.h" +#include "google/protobuf/stubs/stringpiece.h" + +namespace cloud { +namespace crypto { +namespace tink { + +/////////////////////////////////////////////////////////////////////////////// +// The interface for authenticated encryption with additional authenticated +// data. Implementations of this interface are secure against adaptive +// chosen ciphertext attacks. Encryption with additional data ensures +// authenticity and integrity of that data, but not its secrecy. +// (see RFC 5116, https://tools.ietf.org/html/rfc5116) +class Aead { + public: + // Encrypts 'plaintext' with 'additional_data' as additional + // authenticated data, and returns the resulting ciphertext. + // The ciphertext allows for checking authenticity and integrity + // of the additional data , but does not guarantee its secrecy. + virtual util::StatusOr<std::string> Encrypt( + const google::protobuf::StringPiece& plaintext, + const google::protobuf::StringPiece& additional_data) const = 0; + + // Decrypts 'ciphertext' with 'additional_data' as additional + // authenticated data, and returns the resulting plaintext. + // The decryption verifies the authenticity and integrity + // of the additional data, but there are no guarantees wrt. secrecy + // of that data. + virtual util::StatusOr<std::string> Decrypt( + const google::protobuf::StringPiece& ciphertext, + const google::protobuf::StringPiece& additional_data) const = 0; + + virtual ~Aead() {} + + // TODO(przydatek): add asynchronous API. +}; + +} // namespace tink +} // namespace crypto +} // namespace cloud + +#endif // TINK_AEAD_H_ diff --git a/cc/aead_factory.h b/cc/aead_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..ee441c8b93e3ead18f6b9eeb0f93d8d5cd3c224f --- /dev/null +++ b/cc/aead_factory.h @@ -0,0 +1,88 @@ +// 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_AEAD_FACTORY_H_ +#define TINK_AEAD_FACTORY_H_ + +#include "cc/aead.h" +#include "cc/key_manager.h" +#include "cc/keyset_handle.h" +#include "cc/util/errors.h" +#include "cc/util/statusor.h" + +namespace cloud { +namespace crypto { +namespace tink { + +/////////////////////////////////////////////////////////////////////////////// +// AeadFactory allows obtaining a primitive from a KeysetHandle. +// +// AeadFactory gets primitives from the Registry. The factory allows +// initalizing the Registry with native key types and their managers +// that Tink supports out of the box. These key types are divided in +// two groups: +// +// - standard: secure and safe to use in new code. Over time, with +// new developments in cryptanalysis and computing power, some +// standard key types might become legacy. +// +// - legacy: deprecated and insecure or obsolete, should not be used +// in new code. Existing users should upgrade to one of the standard +// key types. +// +// This divison allows for gradual retiring insecure or +// obsolete key types. +// +// For example, here is how one can obtain and use an Aead primitive: +// +// AeadFactory.RegisterStandardKeyTypes(); +// KeysetHandle keyset_handle = ...; +// Aead aead = AeadFactory.GetPrimitive(keyset_handle); +// string plaintext = ...; +// string aad = ...; +// string ciphertext = aead.Encrypt(plaintext, aad).ValueOrDie(); +// +class AeadFactory { + public: + // Registers standard Aead key types and their managers with the Registry. + static util::Status RegisterStandardKeyTypes(); + + // Registers legacy Aead key types and their managers with the Registry. + static util::Status RegisterLegacyKeyTypes(); + + // Returns an Aead-primitive that uses key material from the keyset + // specified via 'keyset_handle'. + static util::StatusOr<std::unique_ptr<Aead>> GetPrimitive( + const KeysetHandle& keyset_handle) { + return util::Status::UNKNOWN; + } + + // Returns an Aead-primitive that uses key material from the keyset + // specified via 'keyset_handle' and is instantiated by the given + // 'custom_key_manager' (instead of the key manager from the Registry). + static util::StatusOr<std::unique_ptr<Aead>> GetPrimitive( + const KeysetHandle& keyset_handle, + const KeyManager<Aead>& custom_key_manager) { + return util::Status::UNKNOWN; + } + +}; + +} // namespace tink +} // namespace crypto +} // namespace cloud + +#endif // TINK_AEAD_FACTORY_H_ diff --git a/cc/core/aead_factory.cc b/cc/core/aead_factory.cc new file mode 100644 index 0000000000000000000000000000000000000000..4ff5c68dcc19786dd45fe9a19f9280f4b6792edb --- /dev/null +++ b/cc/core/aead_factory.cc @@ -0,0 +1,47 @@ +// 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 "cc/aead_factory.h" + +#include "cc/aead.h" +#include "cc/util/errors.h" +#include "cc/util/status.h" +#include "cc/util/statusor.h" +#include "proto/tink.pb.h" + +namespace cloud { +namespace crypto { +namespace tink { + +// static +util::Status AeadFactory::RegisterStandardKeyTypes() { + return util::Status(util::error::UNIMPLEMENTED, "Not implemented yet."); +} + +// static +util::Status AeadFactory::RegisterLegacyKeyTypes() { + return util::Status(util::error::UNIMPLEMENTED, "Not implemented yet."); +} + +// static +util::StatusOr<std::unique_ptr<Aead>> GetPrimitive( + const KeysetHandle& keyset_handle) { + return util::Status::UNKNOWN; +} + +} // namespace tink +} // namespace crypto +} // namespace cloud diff --git a/cc/core/aead_factory_test.cc b/cc/core/aead_factory_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..eea4e4015cd4d21985eee1965222a4563a7498e4 --- /dev/null +++ b/cc/core/aead_factory_test.cc @@ -0,0 +1,56 @@ +// 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 "cc/aead.h" +#include "cc/aead_factory.h" +#include "cc/util/status.h" +#include "cc/util/statusor.h" +#include "gtest/gtest.h" +#include "proto/tink.pb.h" + +using google::cloud::crypto::tink::Keyset; +using util::Status; + +namespace cloud { +namespace crypto { +namespace tink { +namespace { + +class AeadFactoryTest : public ::testing::Test { + protected: + void SetUp() override { + } + void TearDown() override { + } +}; + +TEST_F(AeadFactoryTest, testBasic) { + EXPECT_EQ(util::error::UNIMPLEMENTED, + AeadFactory::RegisterStandardKeyTypes().error_code()); + EXPECT_EQ(util::error::UNIMPLEMENTED, + AeadFactory::RegisterLegacyKeyTypes().error_code()); +} + +} // namespace +} // namespace tink +} // namespace crypto +} // namespace cloud + + +int main(int ac, char* av[]) { + testing::InitGoogleTest(&ac, av); + return RUN_ALL_TESTS(); +} diff --git a/cc/tink_proto_test.cc b/cc/core/keyset_handle.cc similarity index 60% rename from cc/tink_proto_test.cc rename to cc/core/keyset_handle.cc index f433692b85bd081d71ec747aba062bcd547c6704..240823b48c6b53db323b0099be8443b5fbdfa0ac 100644 --- a/cc/tink_proto_test.cc +++ b/cc/core/keyset_handle.cc @@ -4,7 +4,7 @@ // 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 +// 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, @@ -12,30 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. // -//////////////////////////////////////////////////////////////////////////////// - -#include "gtest/gtest.h" +/////////////////////////////////////////////////////////////////////////////// +#include "cc/keyset_handle.h" #include "proto/tink.pb.h" using google::cloud::crypto::tink::Keyset; -class TinkProtoTest : public ::testing::Test { - protected: - virtual void SetUp() { - } +namespace cloud { +namespace crypto { +namespace tink { - virtual void TearDown() { - } -}; +KeysetHandle::KeysetHandle(const Keyset& keyset) : keyset_(keyset) {} -TEST_F(TinkProtoTest, testKeysetBasic) { - Keyset keyset; - keyset.set_primary_key_id(1); - EXPECT_EQ(1, keyset.primary_key_id()); +const Keyset& KeysetHandle::get_keyset() { + return keyset_; } -int main(int ac, char* av[]) { - testing::InitGoogleTest(&ac, av); - return RUN_ALL_TESTS(); -} +} // namespace tink +} // namespace crypto +} // namespace cloud diff --git a/cc/core/mac_factory.cc b/cc/core/mac_factory.cc new file mode 100644 index 0000000000000000000000000000000000000000..1130a705488f4aa3cffcf2ce8deb90222e1124ae --- /dev/null +++ b/cc/core/mac_factory.cc @@ -0,0 +1,40 @@ +// 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 "cc/mac_factory.h" + +#include "cc/mac.h" +#include "google/protobuf/stubs/stringpiece.h" +#include "proto/tink.pb.h" + +namespace cloud { +namespace crypto { +namespace tink { + + +// static +util::Status MacFactory::RegisterStandardKeyTypes() { + return util::Status(util::error::UNIMPLEMENTED, "Not implemented yet."); +} + +// static +util::Status MacFactory::RegisterLegacyKeyTypes() { + return util::Status(util::error::UNIMPLEMENTED, "Not implemented yet."); +} + +} // namespace tink +} // namespace crypto +} // namespace cloud diff --git a/cc/ecdsa_proto_test.cc b/cc/core/mac_factory_test.cc similarity index 54% rename from cc/ecdsa_proto_test.cc rename to cc/core/mac_factory_test.cc index 8d407a693291a1248d43bea137815ea1a7606a45..d5ab26ce70f26edf982236750f8e8ab786f276a0 100644 --- a/cc/ecdsa_proto_test.cc +++ b/cc/core/mac_factory_test.cc @@ -14,27 +14,42 @@ // //////////////////////////////////////////////////////////////////////////////// +#include "cc/mac.h" +#include "cc/mac_factory.h" +#include "cc/util/status.h" +#include "cc/util/statusor.h" #include "gtest/gtest.h" +#include "proto/tink.pb.h" -#include "proto/ecdsa.pb.h" +using google::cloud::crypto::tink::Keyset; +using util::Status; -using google::cloud::crypto::tink::EcdsaPublicKey; +namespace cloud { +namespace crypto { +namespace tink { +namespace { -class EcdsaProtoTest : public ::testing::Test { +class MacFactoryTest : public ::testing::Test { protected: - virtual void SetUp() { + void SetUp() override { } - - virtual void TearDown() { + void TearDown() override { } }; -TEST_F(EcdsaProtoTest, testEcdsaPublicKey) { - EcdsaPublicKey pubKey; - pubKey.set_version(1); - EXPECT_EQ(1, pubKey.version()); +TEST_F(MacFactoryTest, testBasic) { + EXPECT_EQ(util::error::UNIMPLEMENTED, + MacFactory::RegisterStandardKeyTypes().error_code()); + EXPECT_EQ(util::error::UNIMPLEMENTED, + MacFactory::RegisterLegacyKeyTypes().error_code()); } +} // namespace +} // namespace tink +} // namespace crypto +} // namespace cloud + + int main(int ac, char* av[]) { testing::InitGoogleTest(&ac, av); return RUN_ALL_TESTS(); diff --git a/cc/core/registry_test.cc b/cc/core/registry_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..da6d86feaeaf8e9f227084386cd2bd8ffa21e494 --- /dev/null +++ b/cc/core/registry_test.cc @@ -0,0 +1,113 @@ +// 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 <vector> + +#include "cc/aead.h" +#include "cc/registry.h" +#include "cc/util/status.h" +#include "cc/util/statusor.h" +#include "google/protobuf/message_lite.h" +#include "gtest/gtest.h" +#include "proto/aes_ctr_hmac_aead.pb.h" +#include "proto/aes_gcm.pb.h" +#include "proto/tink.pb.h" + + +using google::cloud::crypto::tink::AesCtrHmacAeadKey; +using google::cloud::crypto::tink::AesCtrHmacAeadKeyFormat; +using google::cloud::crypto::tink::AesGcmKey; +using google::cloud::crypto::tink::AesGcmKeyFormat; +using google::cloud::crypto::tink::Keyset; +using google::protobuf::MessageLite; +using util::Status; + +namespace cloud { +namespace crypto { +namespace tink { +namespace { + +class RegistryTest : public ::testing::Test { + protected: + void SetUp() override { + } + void TearDown() override { + } +}; + +template <class K, class F> +class TestAeadKeyManager : + public KeyManager<Aead> { + public: + TestAeadKeyManager(const std::string& key_type) { + key_types_.push_back(key_type); + } + + util::StatusOr<std::unique_ptr<Aead>> + GetPrimitive(const MessageLite& key) const override { + return util::Status::UNKNOWN; + } + + util::Status NewKey(const MessageLite& key_format, MessageLite* key) const override { + return util::Status::UNKNOWN; + } + + const std::vector<std::string>& get_supported_key_types() const override { + return key_types_; + } + private: + std::vector<std::string> key_types_; +}; + +TEST_F(RegistryTest, testBasic) { + Registry& registry = Registry::get_default_registry(); + std::string key_type_1 = AesCtrHmacAeadKey::descriptor()->full_name(); + std::string key_type_2 = AesGcmKey::descriptor()->full_name(); + auto manager_result = registry.get_manager<Aead>(key_type_1); + EXPECT_FALSE(manager_result.ok()); + EXPECT_EQ(util::error::NOT_FOUND, + manager_result.status().error_code()); + + registry.RegisterKeyManager(key_type_1, + new TestAeadKeyManager<AesCtrHmacAeadKey, + AesCtrHmacAeadKeyFormat>(key_type_1)); + registry.RegisterKeyManager(key_type_2, + new TestAeadKeyManager<AesGcmKey, + AesGcmKeyFormat>(key_type_2)); + + manager_result = registry.get_manager<Aead>(key_type_1); + EXPECT_TRUE(manager_result.ok()); + auto manager = manager_result.ValueOrDie(); + EXPECT_TRUE(manager->DoesSupport(key_type_1)); + EXPECT_FALSE(manager->DoesSupport(key_type_2)); + + manager_result = registry.get_manager<Aead>(key_type_2); + EXPECT_TRUE(manager_result.ok()); + manager = manager_result.ValueOrDie(); + EXPECT_TRUE(manager->DoesSupport(key_type_2)); + EXPECT_FALSE(manager->DoesSupport(key_type_1)); +} + +} // namespace +} // namespace tink +} // namespace crypto +} // namespace cloud + + +int main(int ac, char* av[]) { + testing::InitGoogleTest(&ac, av); + return RUN_ALL_TESTS(); +} diff --git a/cc/key_manager.h b/cc/key_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..90c5aeb3b0edccfff227798c8bd4bd486559945d --- /dev/null +++ b/cc/key_manager.h @@ -0,0 +1,70 @@ +// 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 <algorithm> +#include <vector> + +#ifndef TINK_KEY_MANAGER_H_ +#define TINK_KEY_MANAGER_H_ + +#include "cc/util/errors.h" +#include "cc/util/status.h" +#include "cc/util/statusor.h" +#include "google/protobuf/message_lite.h" +#include "proto/tink.pb.h" + +namespace cloud { +namespace crypto { +namespace tink { + +/** + * KeyManager "understands" keys of a specific key types: it can + * generate keys of a supported type and create primitives for + * supported keys. A key type is identified by the global name of the + * protocol buffer that holds the corresponding key material, and is + * given by type_url-field of KeyData-protocol buffer. + * + * - P: the primitive implemented by keys understood by this manager + */ +template<class P> +class KeyManager { + public: + // Constructs an instance of P for the given 'key'. + virtual util::StatusOr<std::unique_ptr<P>> + GetPrimitive(const google::protobuf::MessageLite& key) const = 0; + + // Generates a new random key, based on the specified 'key_format'. + virtual util::Status NewKey( + const google::protobuf::MessageLite& key_format, + google::protobuf::MessageLite* key) const = 0; + + // Returns a vector of type_url-strings that identify the key types + // of keys handled by this manager. + virtual const std::vector<std::string>& get_supported_key_types() const = 0; + + bool DoesSupport(const std::string& key_type) const { + auto types = get_supported_key_types(); + return (std::find(types.begin(), types.end(), key_type) != types.end()); + } + + virtual ~KeyManager<P>() {} +}; + +} // namespace tink +} // namespace crypto +} // namespace cloud + +#endif // TINK_KEY_MANAGER_H_ diff --git a/cc/keyset_handle.h b/cc/keyset_handle.h new file mode 100644 index 0000000000000000000000000000000000000000..7860b16d563622d7c340f56517ff027596a57e28 --- /dev/null +++ b/cc/keyset_handle.h @@ -0,0 +1,48 @@ +// 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 "google/protobuf/stubs/statusor.h" +#include "google/protobuf/stubs/stringpiece.h" +#include "proto/tink.pb.h" + +namespace cloud { +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: + // TODO(przydatek): refactor to ensure that creation KeysetHandle-objects + // can be controlled (as in Java). + KeysetHandle(const google::cloud::crypto::tink::Keyset& keyset); + const google::cloud::crypto::tink::Keyset& get_keyset(); + + private: + google::cloud::crypto::tink::Keyset keyset_; +}; + +} // namespace tink +} // namespace crypto +} // namespace cloud + +#endif // TINK_KEYSET_HANDLE_H_ diff --git a/cc/mac.h b/cc/mac.h new file mode 100644 index 0000000000000000000000000000000000000000..73f21a0c334639229ad9db24b206fcd2ff91c1eb --- /dev/null +++ b/cc/mac.h @@ -0,0 +1,53 @@ +// 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_MAC_H_ +#define TINK_MAC_H_ + +#include "cc/util/status.h" +#include "cc/util/statusor.h" +#include "google/protobuf/stubs/stringpiece.h" + +namespace cloud { +namespace crypto { +namespace tink { + +/////////////////////////////////////////////////////////////////////////////// +// Interface for MACs (Message Authentication Codes). +// This interface should be used for authentication only, and not for other +// purposes (e.g., it should not be used to generate pseudorandom bytes). +class Mac { + public: + // Computes and returns the message authentication code (MAC) for 'data'. + virtual util::StatusOr<std::string> ComputeMac( + google::protobuf::StringPiece data) const = 0; + + // Verifies if 'mac' is a correct authentication code (MAC) for 'data'. + // Returns Status::OK if 'mac' is correct, and a non-OK-Status otherwise. + virtual util::Status VerifyMac( + google::protobuf::StringPiece mac, + google::protobuf::StringPiece data) const = 0; + + virtual ~Mac() {} + + // TODO(przydatek): add asynchronous API. +}; + +} // namespace tink +} // namespace crypto +} // namespace cloud + +#endif // TINK_MAC_H_ diff --git a/cc/mac_factory.h b/cc/mac_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..d117cabc39ff5ee9e9426688128999ea0a2e3c8a --- /dev/null +++ b/cc/mac_factory.h @@ -0,0 +1,86 @@ +// 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_MAC_FACTORY_H_ +#define TINK_MAC_FACTORY_H_ + +#include "cc/key_manager.h" +#include "cc/keyset_handle.h" +#include "cc/mac.h" +#include "cc/util/status.h" +#include "cc/util/statusor.h" +#include "proto/tink.pb.h" + +namespace cloud { +namespace crypto { +namespace tink { + +/////////////////////////////////////////////////////////////////////////////// +// MacFactory allows obtaining a primitive from a KeysetHandle. +// +// MacFactory gets primitives from the Registry. The factory allows +// initalizing the Registry with native key types and their managers +// that Tink supports out of the box. These key types are divided in +// two groups: +// +// - standard: secure and safe to use in new code. Over time, with new +// developments in cryptanalysis and computing power, some standard +// key types might become legacy. +// +// - legacy: deprecated and insecure or obsolete, should not be used +// in new code. Existing users should upgrade to one of the +// standard key types. +// +// This divison allows for gradual retiring insecure or obsolete key types. +// +// For example, here is how one can obtain and use a Mac primitive: +// +// MacFactory.RegisterStandardKeyTypes(); +// KeysetHandle keyset_handle = ...; +// Mac mac = MacFactory.GetPrimitive(keyset_handle); +// string data = ...; +// string tag = mac.ComputeMac(data).ValueOrDie(); +// +class MacFactory { + public: + // Registers standard Mac key types and their managers with the Registry. + static util::Status RegisterStandardKeyTypes(); + + // Registers legacy Mac key types and their managers with the Registry. + static util::Status RegisterLegacyKeyTypes(); + + // Returns a Mac-primitive that uses key material from the keyset + // specified via 'keyset_handle'. + static util::StatusOr<std::unique_ptr<Mac>> GetPrimitive( + const KeysetHandle& keyset_handle) { + return util::Status::UNKNOWN; + } + + // Returns a Mac-primitive that uses key material from the keyset + // specified via 'keyset_handle' and is instantiated by the given + // 'custom_key_manager' (instead of the key manager from the Registry). + static util::StatusOr<std::unique_ptr<Mac>> GetPrimitive( + const KeysetHandle& keyset_handle, + const KeyManager<Mac>& custom_key_manager) { + return util::Status::UNKNOWN; + } +}; + +} // namespace tink +} // namespace crypto +} // namespace cloud + +#endif // TINK_MAC_FACTORY_H_ diff --git a/cc/public/aead.h b/cc/public/aead.h deleted file mode 100644 index d0cf6983820b8ffff3273d1d76ea05de9311a69b..0000000000000000000000000000000000000000 --- a/cc/public/aead.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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. - */ -// AEAD primitive (Authenticated Encryption with Associated Data, RFC 5116). -// TODO(przydatek): add documentation. - -#ifndef TINK_PUBLIC_AEAD_H_ -#define TINK_PUBLIC_AEAD_H_ - -#include "google/protobuf/stubs/stringpiece.h" -#include "google/protobuf/stubs/statusor.h" - -namespace cloud { -namespace crypto { -namespace tink { - -using google::protobuf; - -class Aead { - public: - virtual util::StatusOr<std::string> Encrypt( - StringPiece plaintext, StringPiece associated_data) const = 0; - virtual util::StatusOr<std::string> Decrypt( - StringPiece ciphertext, StringPiece associated_data) const = 0; - virtual ~Aead() {} -}; - -} // namespace tink -} // namespace crypto -} // namespace cloud - -#endif // TINK_PUBLIC_AEAD_H_ diff --git a/cc/registry.h b/cc/registry.h new file mode 100644 index 0000000000000000000000000000000000000000..980c2c601312d91135520c7b7599347c85ae708a --- /dev/null +++ b/cc/registry.h @@ -0,0 +1,135 @@ +// 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_REGISTRY_H_ +#define TINK_REGISTRY_H_ + +#include <typeinfo> +#include <unordered_map> + +#include "cc/key_manager.h" +#include "cc/util/errors.h" +#include "cc/util/status.h" +#include "google/protobuf/message_lite.h" +#include "google/protobuf/stubs/singleton.h" +#include "google/protobuf/stubs/stringpiece.h" +#include "proto/tink.pb.h" + +namespace cloud { +namespace crypto { +namespace tink { + +template <class P> +void delete_manager(void* t) { + delete static_cast<KeyManager<P>*>(t); +} + +// Registry for KeyMangers. +// +// It is essentially a big container (map) that for each supported key +// type holds a corresponding KeyManager object, which "understands" +// the key type (i.e. the KeyManager can instantiate the primitive +// corresponding to given key, or can generate new keys of the +// supported key type). Registry is initialized at startup, and is +// later used to instantiate primitives for given keys or keysets. +// Keeping KeyManagers for all primitives in a single Registry (rather +// than having a separate KeyManager per primitive) enables modular +// construction of compound primitives from "simple" ones, e.g., +// AES-CTR-HMAC AEAD encryption uses IND-CPA encryption and a MAC. +// +// Note that regular users will usually not work directly with +// Registry, but rather via primitive factories, which in the +// background query the Registry for specific KeyManagers. Registry +// is public though, to enable configurations with custom primitives +// and KeyManagers. +class Registry { + public: + static Registry& get_default_registry() { + return *(default_registry_.get()); + } + + // Registers the given 'manager' for the key type identified by 'type_url'. + // Takes ownership of 'manager', which must be non-nullptr. + template <class P> + util::Status RegisterKeyManager(const std::string& type_url, + KeyManager<P>* manager) { + std::unique_ptr<void, void(*)(void*)> + entry(manager, delete_manager<P>); + if (!manager->DoesSupport(type_url)) { + return ToStatusF(util::error::INVALID_ARGUMENT, + "The manager does not support type '%s'.", + type_url.c_str()); + } + auto curr_manager = type_to_manager_map_.find(type_url); + if (curr_manager != type_to_manager_map_.end()) { + return ToStatusF(util::error::ALREADY_EXISTS, + "A manager for type '%s' has been already registered.", + type_url.c_str()); + } + type_to_manager_map_.insert( + std::make_pair(type_url, std::move(entry))); + type_to_primitive_map_.insert( + std::make_pair(type_url, typeid(P).name())); + return util::Status::OK; + } + + // Returns a key manager for the given type_url (if any found). + // Keeps the ownership of the manager. + // TODO(przydatek): consider changing return value to + // StatusOr<std::reference_wrapper<KeyManager<P>>> + // (cannot return reference directly, as StatusOr does not support it, + // see https://goo.gl/x0ymDz) + template <class P> + util::StatusOr<const KeyManager<P>*> get_manager( + const std::string& type_url) { + auto manager_entry = type_to_manager_map_.find(type_url); + if (manager_entry == type_to_manager_map_.end()) { + return ToStatusF(util::error::NOT_FOUND, + "No manager for type '%s' has been registered.", + type_url.c_str()); + } + if (type_to_primitive_map_[type_url] != typeid(P).name()) { + return ToStatusF(util::error::INVALID_ARGUMENT, + "Wrong Primitive type for key type '%s': " + "got '%s', expected '%s'", + type_url.c_str(), + typeid(P).name(), + type_to_primitive_map_[type_url]); + } + return static_cast<KeyManager<P>*>(manager_entry->second.get()); + } + + protected: + friend class google::protobuf::internal::Singleton<Registry>; + Registry() {} + + private: + static google::protobuf::internal::Singleton<Registry> default_registry_; + // TODO(przydatek): change to tbb::concurrent_unordered_map for thread safety + // (https://www.threadingbuildingblocks.org/) + typedef std::unordered_map<std::string, std::unique_ptr<void, void(*)(void*)>> + TypeToManagerMap; + typedef std::unordered_map<std::string, const char*> + TypeToPrimitiveMap; + TypeToManagerMap type_to_manager_map_; + TypeToPrimitiveMap type_to_primitive_map_; +}; + +} // namespace tink +} // namespace crypto +} // namespace cloud + +#endif // TINK_REGISTRY_H_ diff --git a/cc/util/BUILD b/cc/util/BUILD new file mode 100644 index 0000000000000000000000000000000000000000..3dbe97d293dc3bb1cf5edfac3d54fc206729b252 --- /dev/null +++ b/cc/util/BUILD @@ -0,0 +1,42 @@ +licenses(["notice"]) # Apache 2.0 + +package(default_visibility = ["//cc:__subpackages__"]) + +cc_library( + name = "errors", + srcs = ["errors.cc"], + hdrs = ["errors.h"], + deps = [ + ":status", + ], +) + +cc_library( + name = "status", + srcs = ["status.cc"], + hdrs = ["status.h"], +) + +cc_library( + name = "statusor", + srcs = ["statusor.h"], + hdrs = ["statusor.h"], + deps = [ + ":status", + ], +) + +# tests + +cc_test( + name = "errors_test", + size = "small", + srcs = ["errors_test.cc"], + copts = ["-Iexternal/gtest/include"], + linkopts = ["-lpthread"], + deps = [ + ":errors", + ":status", + "@gtest//:gtest", + ], +) diff --git a/cc/util/errors.cc b/cc/util/errors.cc new file mode 100644 index 0000000000000000000000000000000000000000..32b679806784dafbc583b8dcc15619b831d086f5 --- /dev/null +++ b/cc/util/errors.cc @@ -0,0 +1,42 @@ +// 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 <stdarg.h> + +#include "cc/util/status.h" + +using util::error::Code; +using util::Status; + +namespace cloud { +namespace crypto { +namespace tink { + +// Construct a Status object given a printf-style va list. +Status ToStatusF(Code code, const char* format, ...) { + va_list ap; + va_start(ap, format); + char* p; + vasprintf(&p, format, ap); + va_end(ap); + Status status(code, p); + free(p); + return status; +} + +} // namespace tink +} // namespace crypto +} // namespace cloud diff --git a/cc/util/errors.h b/cc/util/errors.h new file mode 100644 index 0000000000000000000000000000000000000000..e1a76637d8c2c45d0f93256407a37f4a74529d80 --- /dev/null +++ b/cc/util/errors.h @@ -0,0 +1,39 @@ +// 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_UTIL_ERRORS_H_ +#define TINK_UTIL_ERRORS_H_ + +#include "cc/util/status.h" + +// from #include "absl/base/port.h" +#define PRINTF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__ (__printf__, string_index, first_to_check))) + +namespace cloud { +namespace crypto { +namespace tink { + +// Constructs a Status object given a printf-style va list. +util::Status ToStatusF( + util::error::Code code, const char* format, ...) + PRINTF_ATTRIBUTE(2, 3); + +} // namespace tink +} // namespace crypto +} // namespace cloud + +#endif // TINK_UTIL_ERRORS_H_ diff --git a/cc/util/errors_test.cc b/cc/util/errors_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..f735bbced2f8ea24ea0c93396883ebe3512a8f70 --- /dev/null +++ b/cc/util/errors_test.cc @@ -0,0 +1,60 @@ +// 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 <stdarg.h> + +#include "gtest/gtest.h" +#include "cc/util/errors.h" +#include "cc/util/status.h" + +namespace cloud { +namespace crypto { +namespace tink { +namespace { + +class ErrorsTest : public ::testing::Test { + protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(ErrorsTest, ToStatusFTest) { + const char* const msg1 = "test message 1"; + const char* const msg2 = "test message %s 2 %d"; + util::Status status; + + status = ToStatusF(util::error::OK, msg1); + EXPECT_TRUE(status.ok()); + // if status is OK, error message is ignored + EXPECT_EQ("", status.error_message()); + EXPECT_EQ(util::error::OK, status.error_code()); + + const char* expected_msg2 = "test message asdf 2 42"; + status = ToStatusF(util::error::UNKNOWN, msg2, "asdf", 42); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(expected_msg2, status.error_message()); + EXPECT_EQ(util::error::UNKNOWN, status.error_code()); +} + +} // namespace +} // namespace tink +} // namespace crypto +} // namespace cloud + +int main(int ac, char* av[]) { + testing::InitGoogleTest(&ac, av); + return RUN_ALL_TESTS(); +} diff --git a/cc/util/status.cc b/cc/util/status.cc new file mode 100644 index 0000000000000000000000000000000000000000..8b3b9422672a21485b71c0e0d489f8419dc289c9 --- /dev/null +++ b/cc/util/status.cc @@ -0,0 +1,128 @@ +// 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 <sstream> + +#include "cc/util/status.h" + +using ::std::ostream; +using std::string; + +namespace util { + +namespace { + + +const Status& GetCancelled() { + static const Status status(::util::error::CANCELLED, ""); + return status; +} + +const Status& GetUnknown() { + static const Status status(::util::error::UNKNOWN, ""); + return status; +} + + +} // namespace + + +Status::Status() : code_(::util::error::OK), message_("") { +} + +Status::Status(::util::error::Code error, const string& error_message) + : code_(error), message_(error_message) { + if (code_ == ::util::error::OK) { + message_.clear(); + } +} + +Status::Status(const Status& other) + : code_(other.code_), message_(other.message_) { +} + +Status& Status::operator=(const Status& other) { + code_ = other.code_; + message_ = other.message_; + return *this; +} + +const Status& Status::CANCELLED = GetCancelled(); +const Status& Status::UNKNOWN = GetUnknown(); +const Status& Status::OK = Status(); + +string Status::ToString() const { + if (code_ == ::util::error::OK) { + return "OK"; + } + + std::ostringstream oss; + oss << code_ << ": " << message_; + return oss.str(); +} + +string ErrorCodeString(util::error::Code error) { + switch (error) { + case util::error::OK: + return "OK"; + case util::error::CANCELLED: + return "CANCELLED"; + case util::error::UNKNOWN: + return "UNKNOWN"; + case util::error::INVALID_ARGUMENT: + return "INVALID_ARGUMENT"; + case util::error::DEADLINE_EXCEEDED: + return "DEADLINE_EXCEEDED"; + case util::error::NOT_FOUND: + return "NOT_FOUND"; + case util::error::ALREADY_EXISTS: + return "ALREADY_EXISTS"; + case util::error::PERMISSION_DENIED: + return "PERMISSION_DENIED"; + case util::error::RESOURCE_EXHAUSTED: + return "RESOURCE_EXHAUSTED"; + case util::error::FAILED_PRECONDITION: + return "FAILED_PRECONDITION"; + case util::error::ABORTED: + return "ABORTED"; + case util::error::OUT_OF_RANGE: + return "OUT_OF_RANGE"; + case util::error::UNIMPLEMENTED: + return "UNIMPLEMENTED"; + case util::error::INTERNAL: + return "INTERNAL"; + case util::error::UNAVAILABLE: + return "UNAVAILABLE"; + case util::error::DATA_LOSS: + return "DATA_LOSS"; + } + // Avoid using a "default" in the switch, so that the compiler can + // give us a warning, but still provide a fallback here. + return std::to_string(error); +} + +extern ostream& operator<<(ostream& os, util::error::Code code) { + os << ErrorCodeString(code); + return os; +} + +extern ostream& operator<<(ostream& os, const Status& other) { + os << other.ToString(); + return os; +} + + +} // namespace util diff --git a/cc/util/status.h b/cc/util/status.h new file mode 100644 index 0000000000000000000000000000000000000000..07816bae0db14c157f894103981c15533e96f6a2 --- /dev/null +++ b/cc/util/status.h @@ -0,0 +1,189 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +// This code was unceremoniously lifted from the version at +// github.com/google/lmctfy with a few minor modifications mainly to reduce the +// dependencies. + +#ifndef TINK_UTIL_STATUS_H_ +#define TINK_UTIL_STATUS_H_ + +#include <string> + +namespace util { + +namespace error { + + +// These values match the error codes in the codes.proto file of the original. +enum Code { + // Not an error; returned on success + OK = 0, + + // The operation was cancelled (typically by the caller). + CANCELLED = 1, + + // Unknown error. + UNKNOWN = 2, + + // Client specified an invalid argument. Note that this differs + // from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments + // that are problematic regardless of the state of the system + // (e.g., a malformed file name). + INVALID_ARGUMENT = 3, + + // Deadline expired before operation could complete. + DEADLINE_EXCEEDED = 4, + + // Some requested entity (e.g., file or directory) was not found. + NOT_FOUND = 5, + + // Some entity that we attempted to create (e.g., file or directory) + // already exists. + ALREADY_EXISTS = 6, + + // The caller does not have permission to execute the specified + // operation. + PERMISSION_DENIED = 7, + + // Some resource has been exhausted, perhaps a per-user quota, or + // perhaps the entire file system is out of space. + RESOURCE_EXHAUSTED = 8, + + // Operation was rejected because the system is not in a state + // required for the operation's execution. For example, directory + // to be deleted may be non-empty, an rmdir operation is applied to + // a non-directory, etc. + // + // A litmus test that may help a service implementor in deciding + // between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: + // (a) Use UNAVAILABLE if the client can retry just the failing call. + // (b) Use ABORTED if the client should retry at a higher-level + // (e.g., restarting a read-modify-write sequence). + // (c) Use FAILED_PRECONDITION if the client should not retry until + // the system state has been explicitly fixed. E.g., if an "rmdir" + // fails because the directory is non-empty, FAILED_PRECONDITION + // should be returned since the client should not retry unless + // they have first fixed up the directory by deleting files from it. + FAILED_PRECONDITION = 9, + + // The operation was aborted, typically due to a concurrency issue + // like sequencer check failures, transaction aborts, etc. + // + // See litmus test above for deciding between FAILED_PRECONDITION, + // ABORTED, and UNAVAILABLE. + ABORTED = 10, + + // Operation was attempted past the valid range. E.g., seeking or + // reading past end of file. + // + // Unlike INVALID_ARGUMENT, this error indicates a problem that may + // be fixed if the system state changes. For example, a 32-bit file + // system will generate INVALID_ARGUMENT if asked to read at an + // offset that is not in the range [0,2^32-1], but it will generate + // OUT_OF_RANGE if asked to read from an offset past the current + // file size. + OUT_OF_RANGE = 11, + + // Operation is not implemented or not supported/enabled in this service. + UNIMPLEMENTED = 12, + + // Internal errors. Means some invariants expected by underlying + // system has been broken. If you see one of these errors, + // something is very broken. + INTERNAL = 13, + + // The service is currently unavailable. This is a most likely a + // transient condition and may be corrected by retrying with + // a backoff. + // + // See litmus test above for deciding between FAILED_PRECONDITION, + // ABORTED, and UNAVAILABLE. + UNAVAILABLE = 14, + + // Unrecoverable data loss or corruption. + DATA_LOSS = 15, +}; + + +} // namespace error + + +// A Status is a combination of an error code and a string message (for non-OK +// error codes). +class Status { + public: + // Creates an OK status + Status(); + + // Make a Status from the specified error and message. + Status(::util::error::Code error, const std::string& error_message); + + Status(const Status& other); + Status& operator=(const Status& other); + + // Some pre-defined Status objects + static const Status& OK; // Identical to 0-arg constructor + static const Status& CANCELLED; + static const Status& UNKNOWN; + + // Accessors + bool ok() const { + return code_ == ::util::error::OK; + } + int error_code() const { + return code_; + } + ::util::error::Code CanonicalCode() const { + return code_; + } + const std::string& error_message() const { + return message_; + } + + bool operator==(const Status& x) const; + bool operator!=(const Status& x) const; + + // NoOp + void IgnoreError() const { + } + + std::string ToString() const; + + private: + ::util::error::Code code_; + std::string message_; +}; + +inline bool Status::operator==(const Status& other) const { + return (this->code_ == other.code_) && (this->message_ == other.message_); +} + +inline bool Status::operator!=(const Status& other) const { + return !(*this == other); +} + +extern std::string ErrorCodeString(util::error::Code error); + +extern ::std::ostream& operator<<(::std::ostream& os, + ::util::error::Code code); +extern ::std::ostream& operator<<(::std::ostream& os, const Status& other); + +// Returns an OK status, equivalent to a default constructed instance. +inline Status OkStatus() { return Status(); } + +} // namespace util + +#endif // TINK_UTIL_STATUS_H_ diff --git a/cc/util/statusor.h b/cc/util/statusor.h new file mode 100644 index 0000000000000000000000000000000000000000..b8e889df76f7edee9cbcce326ee5951921eab8dc --- /dev/null +++ b/cc/util/statusor.h @@ -0,0 +1,141 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// 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_UTIL_STATUSOR_H_ +#define TINK_UTIL_STATUSOR_H_ + +#include <utility> + +#include "cc/util/status.h" + +namespace util { + +// A StatusOr holds a Status (in the case of an error), or a value T. +template <typename T> +class StatusOr { + public: + // Has status UNKNOWN. + inline StatusOr(); + + // Builds from a non-OK status. Crashes if an OK status is specified. + inline StatusOr(const ::util::Status& status); // NOLINT + + // Builds from the specified value. + inline StatusOr(const T& value); // NOLINT + inline StatusOr(T&& value); // NOLINT + + // Copy constructor. + inline StatusOr(const StatusOr& other); + + // Move constructor. + inline StatusOr(StatusOr&& other); + + // Conversion copy constructor, T must be copy constructible from U. + template <typename U> + inline StatusOr(const StatusOr<U>& other); + + // Assignment operator. + inline const StatusOr& operator=(const StatusOr& other); + + // Conversion assignment operator, T must be assignable from U + template <typename U> + inline const StatusOr& operator=(const StatusOr<U>& other); + + // Accessors. + inline const ::util::Status& status() const { + return status_; + } + + // Shorthand for status().ok(). + inline bool ok() const { + return status_.ok(); + } + + // Returns value or crashes if ok() is false. + inline const T& ValueOrDie() const { + // CHECK(ok()) << "Attempting to fetch value of non-OK StatusOr"; + return value_; + } + inline T& ValueOrDie() { + // CHECK(ok()) << "Attempting to fetch value of non-OK StatusOr"; + return value_; + } + + template <typename U> + friend class StatusOr; + + private: + Status status_; + T value_; +}; + +// Implementation. + +template <typename T> +inline StatusOr<T>::StatusOr() : status_(::util::error::UNKNOWN, "") { +} + +template <typename T> +inline StatusOr<T>::StatusOr(const ::util::Status& status) : status_(status) { + // CHECK(!status.ok()) << "::util::OkStatus() is not a valid argument to StatusOr"; +} + +template <typename T> +inline StatusOr<T>::StatusOr(const T& value) : value_(value) { +} + +template <typename T> +inline StatusOr<T>::StatusOr(T&& value) : value_(std::move(value)) { +} + +template <typename T> +inline StatusOr<T>::StatusOr(const StatusOr& other) + : status_(other.status_), value_(other.value_) { +} + +template <typename T> +inline StatusOr<T>::StatusOr(StatusOr&& other) + : status_(other.status_), value_(std::move(other.value_)) { +} + +template <typename T> +template <typename U> +inline StatusOr<T>::StatusOr(const StatusOr<U>& other) + : status_(other.status_), value_(other.value_) { +} + +template <typename T> +inline const StatusOr<T>& StatusOr<T>::operator=(const StatusOr& other) { + status_ = other.status_; + if (status_.ok()) { + value_ = other.value_; + } + return *this; +} + +template <typename T> +template <typename U> +inline const StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) { + status_ = other.status_; + if (status_.ok()) { + value_ = other.value_; + } + return *this; +} + +} // namespace util + +#endif // TINK_UTIL_STATUSOR_H_