From 11966745c00000261dfc6345b44dc1a7d7fe8f71 Mon Sep 17 00:00:00 2001
From: sschmieg <sschmieg@google.com>
Date: Fri, 30 Aug 2019 13:27:07 -0700
Subject: [PATCH] Defining an interface for a StreamingMac primitive, returning
 OutputStream that defer the computation until closed.

PiperOrigin-RevId: 266444142
---
 cc/BUILD.bazel                 |  31 ++++++++++
 cc/CMakeLists.txt              |  23 +++++++
 cc/output_stream_with_result.h | 110 +++++++++++++++++++++++++++++++++
 cc/streaming_mac.h             |  53 ++++++++++++++++
 4 files changed, 217 insertions(+)
 create mode 100644 cc/output_stream_with_result.h
 create mode 100644 cc/streaming_mac.h

diff --git a/cc/BUILD.bazel b/cc/BUILD.bazel
index a13346625..0e93059fc 100644
--- a/cc/BUILD.bazel
+++ b/cc/BUILD.bazel
@@ -39,6 +39,7 @@ PUBLIC_APIS = [
     "mac_config.h",
     "mac_factory.h",
     "mac_key_templates.h",
+    "output_stream_with_result.h",
     "output_stream.h",
     "public_key_sign.h",
     "public_key_sign_factory.h",
@@ -51,6 +52,7 @@ PUBLIC_APIS = [
     "streaming_aead.h",
     "streaming_aead_config.h",
     "streaming_aead_key_templates.h",
+    "streaming_mac.h",
     "tink_config.h",
     "version.h",
 ]
@@ -72,11 +74,13 @@ PUBLIC_API_DEPS = [
     ":keyset_writer",
     ":kms_client",
     ":mac",
+    ":output_stream_with_result",
     ":output_stream",
     ":primitive_set",
     ":public_key_sign",
     ":public_key_verify",
     ":streaming_aead",
+    ":streaming_mac",
     ":random_access_stream",
     ":registry",
     ":registry_impl",
@@ -159,6 +163,19 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "output_stream_with_result",
+    hdrs = ["output_stream_with_result.h"],
+    include_prefix = "tink",
+    strip_include_prefix = "/cc",
+    visibility = ["//visibility:public"],
+    deps = [
+        ":output_stream",
+        "//cc/util:status",
+        "//cc/util:statusor",
+    ],
+)
+
 cc_library(
     name = "aead",
     hdrs = ["aead.h"],
@@ -198,6 +215,20 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "streaming_mac",
+    hdrs = ["streaming_mac.h"],
+    include_prefix = "tink",
+    strip_include_prefix = "/cc",
+    visibility = ["//visibility:public"],
+    deps = [
+        ":output_stream_with_result",
+        "//cc/util:status",
+        "//cc/util:statusor",
+        "@com_google_absl//absl/strings",
+    ],
+)
+
 cc_library(
     name = "hybrid_decrypt",
     hdrs = ["hybrid_decrypt.h"],
diff --git a/cc/CMakeLists.txt b/cc/CMakeLists.txt
index 35d36ec13..0f4ffe336 100644
--- a/cc/CMakeLists.txt
+++ b/cc/CMakeLists.txt
@@ -46,6 +46,7 @@ set(TINK_PUBLIC_APIS
   mac_config.h
   mac_factory.h
   mac_key_templates.h
+  output_stream_with_result.h
   output_stream.h
   public_key_sign.h
   public_key_sign_factory.h
@@ -58,6 +59,7 @@ set(TINK_PUBLIC_APIS
   streaming_aead.h
   streaming_aead_config.h
   streaming_aead_key_templates.h
+  streaming_mac.h
   tink_config.h
   "${TINK_VERSION_H}"
 )
@@ -79,6 +81,7 @@ set(TINK_PUBLIC_API_DEPS
   tink::core::keyset_reader
   tink::core::keyset_writer
   tink::core::kms_client
+  tink::core::output_stream_with_result
   tink::core::output_stream
   tink::core::public_key_sign
   tink::core::public_key_verify
@@ -88,6 +91,7 @@ set(TINK_PUBLIC_API_DEPS
   tink::core::registry
   tink::core::registry_impl
   tink::core::streaming_aead
+  tink::core::streaming_mac
   tink::core::version
   tink::aead::aead_config
   tink::aead::aead_factory
@@ -155,6 +159,15 @@ tink_cc_library(
     tink::util::statusor
 )
 
+tink_cc_library(
+  NAME output_stream_with_result
+  SRCS output_stream_with_result.h
+  DEPS
+    tink::core::output_stream_with_result
+    tink::util::status
+    tink::util::statusor
+)
+
 tink_cc_library(
   NAME aead
   SRCS aead.h
@@ -182,6 +195,16 @@ tink_cc_library(
     absl::strings
 )
 
+tink_cc_library(
+  NAME streaming_mac
+  SRCS streaming_mac.h
+  DEPS
+    tink::core::output_stream_with_result
+    tink::util::status
+    tink::util::statusor
+    absl::strings
+)
+
 tink_cc_library(
   NAME hybrid_decrypt
   SRCS hybrid_decrypt.h
diff --git a/cc/output_stream_with_result.h b/cc/output_stream_with_result.h
new file mode 100644
index 000000000..01f330da0
--- /dev/null
+++ b/cc/output_stream_with_result.h
@@ -0,0 +1,110 @@
+// Copyright 2018 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_OUTPUT_STREAM_WITH_RESULT_H_
+#define TINK_OUTPUT_STREAM_WITH_RESULT_H_
+
+#include <type_traits>
+
+#include "tink/output_stream.h"
+#include "tink/util/status.h"
+#include "tink/util/statusor.h"
+
+namespace crypto {
+namespace tink {
+
+// An abstract OutputStream subclass that acts as a sink for data and returns a
+// result after the stream has been closed.
+// The result is only available if the stream was able to close with an ok
+// status. Otherwise, the status of the GetResult call will be the same status
+// as the Close call had. If Close is successful, GetResult is guaranteed to
+// return a valid result.
+// The return type of GetResult will be StatusOr<T>, except for T == Status, in
+// which case the return type is Status.
+template <class T>
+class OutputStreamWithResult : public OutputStream {
+ public:
+  OutputStreamWithResult() : closed_(false) {}
+  ~OutputStreamWithResult() override {}
+
+  // The return type is StatusOr<T> if T != Status, and Status otherwise.
+  using ResultType =
+      typename std::conditional<std::is_same<T, util::Status>::value,
+                                util::Status, util::StatusOr<T>>::type;
+
+  // Get the result associated with this OutputStream. Can only be called on
+  // closed streams, and will otherwise fail with FAILED_PRECONDITION as error
+  // code.
+  // If Close() returned an ok status, this method is guaranteed to contain a
+  // valid result.
+  // The return type is StatusOr<T> if T != Status, and Status otherwise.
+  ResultType GetResult() {
+    if (!closed_) {
+      return util::Status(util::error::FAILED_PRECONDITION,
+                          "Stream is not closed");
+    }
+    return result_;
+  }
+
+  // Close the stream and return the computed result. Equivalent to calling
+  // Close() and GetResult() if Close() returned an OK status.
+  // The return type is StatusOr<T> if T != Status, and Status otherwise.
+  ResultType CloseAndGetResult() {
+    util::Status closing_status = Close();
+    if (!closing_status.ok()) {
+      return closing_status;
+    }
+    return GetResult();
+  }
+
+  // Closes the OutputStream.
+  util::Status Close() final {
+    if (closed_) {
+      return util::Status(util::error::FAILED_PRECONDITION, "Stream closed");
+    }
+    result_ = CloseStreamAndComputeResult();
+    closed_ = true;
+    return result_.status();
+  }
+
+  // Getting the next OutputStream buffer. See OutputStream for detailed
+  // description.
+  crypto::tink::util::StatusOr<int> Next(void** data) final {
+    if (closed_) {
+      return util::Status(util::error::FAILED_PRECONDITION,
+                          "Write on closed Stream");
+    }
+    return NextBuffer(data);
+  }
+
+ protected:
+  // Compute the result for this OutputStream. Safe for thread safety problems,
+  // this method will only be called once.
+  // The return type is StatusOr<T> if T != Status, and Status otherwise.
+  virtual ResultType CloseStreamAndComputeResult() = 0;
+  // Getting the next OutputStream buffer. See OutputStream for detailed
+  // description.
+  virtual util::StatusOr<int> NextBuffer(void** data) = 0;
+
+ private:
+  bool closed_;
+  ResultType result_;
+};
+
+}  // namespace tink
+}  // namespace crypto
+
+#endif  // TINK_OUTPUT_STREAM_WITH_RESULT_H_
diff --git a/cc/streaming_mac.h b/cc/streaming_mac.h
new file mode 100644
index 000000000..037b14895
--- /dev/null
+++ b/cc/streaming_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_STREAMING_MAC_H_
+#define TINK_STREAMING_MAC_H_
+
+#include <memory>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "tink/output_stream_with_result.h"
+#include "tink/util/status.h"
+#include "tink/util/statusor.h"
+
+namespace crypto {
+namespace tink {
+
+///////////////////////////////////////////////////////////////////////////////
+// Interface for Streaming 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 StreamingMac {
+ public:
+  // Returns an ComputeMacOutputStream, which when closed will return the
+  // message authentication code (MAC) of the data put into the stream.
+  virtual util::StatusOr<std::unique_ptr<OutputStreamWithResult<std::string>>>
+  NewComputeMacOutputStream() const = 0;
+
+  // Returns an VerifyMacOutputStream which verifies if 'mac' is a correct
+  // message authentication code (MAC) for the data written to it.
+  virtual util::StatusOr<std::unique_ptr<OutputStreamWithResult<util::Status>>>
+  NewVerifyMacOutputStream(const std::string& mac_value) const = 0;
+
+  virtual ~StreamingMac() {}
+};
+
+}  // namespace tink
+}  // namespace crypto
+
+#endif  // TINK_STREAMING_MAC_H_
-- 
GitLab