Add static factory to EncryptedMessageMaker.

We will swap out instantiations of EncryptedMessageMaker for the use of
this factory which will allow us to transform EncryptedMessageMaker into
an interface with several implementations.

Change-Id: I5016eee81c9630ed544652676731f4e27960e03f
diff --git a/util/BUILD.gn b/util/BUILD.gn
index fb8bfbc..85c2adf 100644
--- a/util/BUILD.gn
+++ b/util/BUILD.gn
@@ -16,6 +16,7 @@
   configs += [ "//third_party/cobalt:cobalt_config" ]
 
   deps = [
+    ":status",
     "//garnet/public/lib/fxl",
     "//third_party/cobalt:cobalt_proto",
   ]
diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt
index 9766584..b38a807 100644
--- a/util/CMakeLists.txt
+++ b/util/CMakeLists.txt
@@ -12,7 +12,9 @@
             encrypted_message_util.cc
             ${COBALT_PROTO_SRCS})
 target_link_libraries(encrypted_message_util
-                      cobalt_crypto)
+                      cobalt_crypto
+                      status
+                      tensorflow_statusor)
 add_cobalt_dependencies(encrypted_message_util)
 
 add_library(pem_util
diff --git a/util/encrypted_message_util.cc b/util/encrypted_message_util.cc
index 3dfdd75..f02972d 100644
--- a/util/encrypted_message_util.cc
+++ b/util/encrypted_message_util.cc
@@ -20,6 +20,8 @@
 #include "./logging.h"
 #include "google/protobuf/message_lite.h"
 #include "util/crypto_util/cipher.h"
+#include "util/status.h"
+#include "util/status_codes.h"
 
 namespace cobalt {
 namespace util {
@@ -41,6 +43,24 @@
   }
 }
 
+tensorflow_statusor::StatusOr<std::unique_ptr<EncryptedMessageMaker>>
+EncryptedMessageMaker::Make(const std::string& public_key_pem,
+                            EncryptedMessage::EncryptionScheme scheme) {
+  if (scheme == EncryptedMessage::NONE) {
+    return Status(INVALID_ARGUMENT,
+                  "EncryptedMessageMaker: encryption_scheme NONE is not "
+                  "allowed in production.");
+  }
+  return std::make_unique<EncryptedMessageMaker>(public_key_pem, scheme);
+}
+
+tensorflow_statusor::StatusOr<std::unique_ptr<EncryptedMessageMaker>>
+EncryptedMessageMaker::MakeAllowUnencrypted(
+    const std::string& public_key_pem,
+    EncryptedMessage::EncryptionScheme scheme) {
+  return std::make_unique<EncryptedMessageMaker>(public_key_pem, scheme);
+}
+
 bool EncryptedMessageMaker::Encrypt(
     const google::protobuf::MessageLite& message,
     EncryptedMessage* encrypted_message) const {
diff --git a/util/encrypted_message_util.h b/util/encrypted_message_util.h
index 11d3271..41c03ef 100644
--- a/util/encrypted_message_util.h
+++ b/util/encrypted_message_util.h
@@ -29,6 +29,7 @@
 
 #include "./encrypted_message.pb.h"
 #include "google/protobuf/message_lite.h"
+#include "third_party/tensorflow_statusor/statusor.h"
 #include "util/crypto_util/cipher.h"
 
 namespace cobalt {
@@ -43,7 +44,24 @@
 // encrypting Envelopes.
 class EncryptedMessageMaker {
  public:
-  // Constructs an EncryptedMessageMaker.
+  // Encrypts a protocol buffer |message| and populates |encrypted_message|
+  // with the result. Returns true for success or false on failure.
+  bool Encrypt(const google::protobuf::MessageLite& message,
+               EncryptedMessage* encrypted_message) const;
+
+  // Make an EncryptedMessageMaker.
+  //
+  // |scheme| specifies which encryption scheme should be used.
+  // The use of EncryptedMessage::NONE is disallowed.
+  //
+  // |public_key_pem| must be appropriate to |scheme|. If |scheme| is
+  // EncryptedMessage::HYBRID_ECDH_V1 then |public_key_pem| must be a PEM
+  // encoding of a public key appropriate for that scheme.
+  static tensorflow_statusor::StatusOr<std::unique_ptr<EncryptedMessageMaker>>
+  Make(const std::string& public_key_pem,
+       EncryptedMessage::EncryptionScheme scheme);
+
+  // Make an EncryptedMessageMaker. (Do not use in production)
   //
   // |scheme| specifies which encryption scheme should be used. As of this
   // writing there are two schemes:
@@ -59,14 +77,14 @@
   // EncryptedMessage::NONE then |public_key_pem| is ignored. If |scheme| is
   // EncryptedMessage::HYBRID_ECDH_V1 then |public_key_pem| must be a PEM
   // encoding of a public key appropriate for that scheme.
+  static tensorflow_statusor::StatusOr<std::unique_ptr<EncryptedMessageMaker>>
+  MakeAllowUnencrypted(const std::string& public_key_pem,
+                       EncryptedMessage::EncryptionScheme scheme);
+
+  // DEPRECATED. Use Make instead.
   EncryptedMessageMaker(const std::string& public_key_pem,
                         EncryptedMessage::EncryptionScheme scheme);
 
-  // Encrypts a protocol buffer |message| and populates |encrypted_message|
-  // with the result. Returns true for success or false on failure.
-  bool Encrypt(const google::protobuf::MessageLite& message,
-               EncryptedMessage* encrypted_message) const;
-
  private:
   std::unique_ptr<crypto::HybridCipher> cipher_;
   EncryptedMessage::EncryptionScheme encryption_scheme_;
diff --git a/util/encrypted_message_util_test.cc b/util/encrypted_message_util_test.cc
index c7bdf38..d311106 100644
--- a/util/encrypted_message_util_test.cc
+++ b/util/encrypted_message_util_test.cc
@@ -37,10 +37,12 @@
   // Make a dummy observation.
   auto observation = MakeDummyObservation("hello");
   // Make an EncryptedMessageMaker that uses the NONE encryption scheme.
-  EncryptedMessageMaker maker("dummy_key", EncryptedMessage::NONE);
+  auto maker = EncryptedMessageMaker::MakeAllowUnencrypted(
+                   "dummy_key", EncryptedMessage::NONE)
+                   .ValueOrDie();
   // Encrypt the dummy observation.
   EncryptedMessage encrypted_message;
-  EXPECT_TRUE(maker.Encrypt(observation, &encrypted_message));
+  EXPECT_TRUE(maker->Encrypt(observation, &encrypted_message));
 
   // Make a MessageDecrypter.
   MessageDecrypter decrypter("dummy_key");
@@ -56,11 +58,13 @@
   auto observation = MakeDummyObservation("hello");
 
   // Make an EncryptedMessageMaker that uses a bad public key.
-  EncryptedMessageMaker maker("dummy_key", EncryptedMessage::HYBRID_ECDH_V1);
+  auto maker = EncryptedMessageMaker::Make(
+                   "dummy_key", EncryptedMessage::HYBRID_ECDH_V1)
+                   .ValueOrDie();
   // Try to encrypt the dummy observation.
   EncryptedMessage encrypted_message;
   // Expect it to fail, but not crash.
-  EXPECT_FALSE(maker.Encrypt(observation, &encrypted_message));
+  EXPECT_FALSE(maker->Encrypt(observation, &encrypted_message));
 }
 
 // Tests the use of the hybrid cipher option.
@@ -73,10 +77,12 @@
   auto observation = MakeDummyObservation("hello");
 
   // Make an EncryptedMessageMaker that uses our real encryption scheme.
-  EncryptedMessageMaker maker(public_key, EncryptedMessage::HYBRID_ECDH_V1);
+  auto maker = EncryptedMessageMaker::Make(
+                   public_key, EncryptedMessage::HYBRID_ECDH_V1)
+                   .ValueOrDie();
   // Encrypt the dummy observation.
   EncryptedMessage encrypted_message;
-  ASSERT_TRUE(maker.Encrypt(observation, &encrypted_message));
+  ASSERT_TRUE(maker->Encrypt(observation, &encrypted_message));
   EXPECT_EQ(32u, encrypted_message.public_key_fingerprint().size());
 
   // Make a MessageDecrypter.
@@ -93,6 +99,16 @@
   EXPECT_FALSE(bad_decrypter.DecryptMessage(encrypted_message, &observation));
 }
 
+// Tests that Make does not allow the NONE scheme.
+TEST(EncryptedMessageUtilTest, DisallowUnencrypted) {
+  // Make a dummy observation.
+  auto observation = MakeDummyObservation("hello");
+  // Try to make an EncryptedMessageMaker that uses the NONE encryption scheme.
+  auto status = EncryptedMessageMaker::Make(
+                   "dummy_key", EncryptedMessage::NONE);
+  EXPECT_EQ(INVALID_ARGUMENT, status.status().error_code());
+}
+
 // Tests that using encryption incorrectly fails but doesn't cause any crashes.
 TEST(EncryptedMessageUtilTest, Crazy) {
   std::string public_key;
@@ -104,18 +120,21 @@
 
   // Make an EncryptedMessageMaker that incorrectly uses the private key
   // instead of the public key
-  EncryptedMessageMaker bad_maker(private_key,
-                                  EncryptedMessage::HYBRID_ECDH_V1);
+  auto bad_maker = EncryptedMessageMaker::Make(
+                       private_key, EncryptedMessage::HYBRID_ECDH_V1)
+                       .ValueOrDie();
+
   // Try to encrypt the dummy observation.
   EncryptedMessage encrypted_message;
   // Expect it to fail, but not crash.
-  EXPECT_FALSE(bad_maker.Encrypt(observation, &encrypted_message));
+  EXPECT_FALSE(bad_maker->Encrypt(observation, &encrypted_message));
 
   // Now make a good EncryptedMessageMaker
-  EncryptedMessageMaker real_maker(public_key,
-                                   EncryptedMessage::HYBRID_ECDH_V1);
+  auto real_maker = EncryptedMessageMaker::Make(
+                        public_key, EncryptedMessage::HYBRID_ECDH_V1)
+                        .ValueOrDie();
   // Encrypt the dummy observation.
-  EXPECT_TRUE(real_maker.Encrypt(observation, &encrypted_message));
+  EXPECT_TRUE(real_maker->Encrypt(observation, &encrypted_message));
 
   // Make a MessageDecrypter that uses the correct private key.
   MessageDecrypter real_decrypter(private_key);