blob: 029c7ec9508f908a960bc753d26cd37d55987ad0 [file] [log] [blame]
// 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 <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