| // Copyright 2019 Google LLC |
| // |
| // 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/subtle/streaming_mac_impl.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.h" |
| #include "openssl/crypto.h" |
| #include "tink/util/status.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace subtle { |
| |
| namespace { |
| constexpr size_t kBufferSize = 4096; |
| } |
| |
| class ComputeMacOutputStream : public OutputStreamWithResult<std::string> { |
| public: |
| explicit ComputeMacOutputStream(std::unique_ptr<StatefulMac> mac) |
| : status_(util::OkStatus()), |
| mac_(std::move(mac)), |
| position_(0), |
| buffer_position_(0), |
| buffer_("") { |
| buffer_.resize(kBufferSize); |
| } |
| |
| util::StatusOr<int> NextBuffer(void** buffer) override; |
| util::StatusOr<std::string> CloseStreamAndComputeResult() override; |
| void BackUp(int count) override; |
| int64_t Position() const override { return position_; } |
| |
| private: |
| void WriteIntoMac(); |
| |
| util::Status status_; |
| const std::unique_ptr<StatefulMac> mac_; |
| int64_t position_; |
| int buffer_position_; |
| std::string buffer_; |
| }; |
| |
| util::StatusOr<std::unique_ptr<OutputStreamWithResult<std::string>>> |
| StreamingMacImpl::NewComputeMacOutputStream() const { |
| util::StatusOr<std::unique_ptr<StatefulMac>> mac_status = |
| mac_factory_->Create(); |
| |
| if (!mac_status.ok()) { |
| return mac_status.status(); |
| } |
| |
| std::unique_ptr<OutputStreamWithResult<std::string>> string_to_return = |
| absl::make_unique<ComputeMacOutputStream>(std::move(mac_status.value())); |
| return std::move(string_to_return); |
| } |
| |
| util::StatusOr<int> ComputeMacOutputStream::NextBuffer(void** buffer) { |
| if (!status_.ok()) { |
| return status_; |
| } |
| WriteIntoMac(); |
| *buffer = &buffer_[0]; |
| position_ += kBufferSize; |
| buffer_position_ = kBufferSize; |
| return buffer_position_; |
| } |
| |
| util::StatusOr<std::string> |
| ComputeMacOutputStream::CloseStreamAndComputeResult() { |
| if (!status_.ok()) { |
| return status_; |
| } |
| WriteIntoMac(); |
| status_ = |
| util::Status(absl::StatusCode::kFailedPrecondition, "Stream Closed"); |
| return mac_->Finalize(); |
| } |
| |
| void ComputeMacOutputStream::BackUp(int count) { |
| count = std::min(count, buffer_position_); |
| buffer_position_ -= count; |
| position_ -= count; |
| } |
| |
| // Writes the data in buffer_ into mac_, and clears buffer_. |
| void ComputeMacOutputStream::WriteIntoMac() { |
| // Remove the suffix of the buffer (all data after buffer_position_). |
| status_ = mac_->Update(absl::string_view(buffer_.data(), buffer_position_)); |
| |
| // Clear the buffer, so that any sensitive information that |
| // was written to the buffer cannot be accessed later. |
| // Write buffer_position_ number of 0's to the buffer, starting from idx 0. |
| buffer_.replace(0, buffer_position_, buffer_position_, 0); |
| } |
| |
| class VerifyMacOutputStream : public OutputStreamWithResult<util::Status> { |
| public: |
| VerifyMacOutputStream(const std::string& expected, |
| std::unique_ptr<StatefulMac> mac) |
| : status_(util::OkStatus()), |
| mac_(std::move(mac)), |
| position_(0), |
| buffer_position_(0), |
| buffer_(""), |
| expected_(expected) { |
| buffer_.resize(kBufferSize); |
| } |
| |
| util::StatusOr<int> NextBuffer(void** buffer) override; |
| |
| util::Status CloseStreamAndComputeResult() override; |
| |
| void BackUp(int count) override; |
| int64_t Position() const override { return position_; } |
| |
| private: |
| void WriteIntoMac(); |
| |
| // Stream status: Initialized as OK, and |
| // changed to ERROR:FAILED_PRECONDITION when the stream is closed. |
| util::Status status_; |
| std::unique_ptr<StatefulMac> mac_; |
| int64_t position_; |
| int buffer_position_; |
| std::string buffer_; |
| std::string expected_; |
| }; |
| |
| util::StatusOr<int> VerifyMacOutputStream::NextBuffer(void** buffer) { |
| if (!status_.ok()) { |
| return status_; |
| } |
| WriteIntoMac(); |
| *buffer = &buffer_[0]; |
| position_ += kBufferSize; |
| buffer_position_ = kBufferSize; |
| return buffer_position_; |
| } |
| |
| util::Status VerifyMacOutputStream::CloseStreamAndComputeResult() { |
| if (!status_.ok()) { |
| return status_; |
| } |
| WriteIntoMac(); |
| status_ = |
| util::Status(absl::StatusCode::kFailedPrecondition, "Stream Closed"); |
| util::StatusOr<std::string> mac_actual = mac_->Finalize(); |
| if (!mac_actual.ok()) { |
| return mac_actual.status(); |
| } |
| if (mac_actual->size() != expected_.size()) { |
| return absl::InvalidArgumentError( |
| absl::StrCat("Invalid MAC size; expected ", expected_.size(), ", got ", |
| mac_actual->size())); |
| } |
| if (!CRYPTO_memcmp(mac_actual->data(), expected_.data(), |
| mac_actual->size())) { |
| return util::OkStatus(); |
| } |
| return absl::InvalidArgumentError("Incorrect MAC"); |
| } |
| |
| void VerifyMacOutputStream::BackUp(int count) { |
| count = std::min(count, buffer_position_); |
| buffer_position_ -= count; |
| position_ -= count; |
| } |
| |
| // Writes the data in buffer_ into mac_, and clears buffer_. |
| void VerifyMacOutputStream::WriteIntoMac() { |
| // Remove the suffix of the buffer (all data after buffer_position_). |
| status_ = mac_->Update(absl::string_view(buffer_.data(), buffer_position_)); |
| |
| // Clear the buffer, so that any sensitive information that |
| // was written to the buffer cannot be accessed later. |
| // Write buffer_position_ number of 0's to the buffer, starting from idx 0. |
| buffer_.replace(0, buffer_position_, buffer_position_, 0); |
| } |
| |
| util::StatusOr<std::unique_ptr<OutputStreamWithResult<util::Status>>> |
| StreamingMacImpl::NewVerifyMacOutputStream(const std::string& mac_value) const { |
| util::StatusOr<std::unique_ptr<StatefulMac>> mac_status = |
| mac_factory_->Create(); |
| if (!mac_status.ok()) { |
| return mac_status.status(); |
| } |
| return std::unique_ptr<OutputStreamWithResult<util::Status>>( |
| absl::make_unique<VerifyMacOutputStream>(mac_value, |
| std::move(mac_status.value()))); |
| } |
| } // namespace subtle |
| } // namespace tink |
| } // namespace crypto |