blob: 8d91050e31007ea6b55e27a13d93d4f0b95b08b9 [file] [log] [blame]
// 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_aead_test_util.h"
#include <sstream>
#include <string>
#include "tink/random_access_stream.h"
#include "tink/subtle/test_util.h"
#include "tink/util/buffer.h"
#include "tink/util/file_random_access_stream.h"
#include "tink/util/istream_input_stream.h"
#include "tink/util/ostream_output_stream.h"
#include "tink/util/status.h"
#include "tink/util/test_util.h"
namespace crypto {
namespace tink {
using ::crypto::tink::test::GetTestFileDescriptor;
using ::crypto::tink::util::IstreamInputStream;
using ::crypto::tink::util::OstreamOutputStream;
using ::crypto::tink::util::Status;
namespace {
// Creates a RandomAccessStream with the specified contents.
std::unique_ptr<RandomAccessStream> GetRandomAccessStreamContaining(
absl::string_view contents) {
static int index = 1;
std::string filename = absl::StrCat("stream_data_file_", index, ".txt");
index++;
int input_fd = GetTestFileDescriptor(filename, contents);
return {absl::make_unique<util::FileRandomAccessStream>(input_fd)};
}
// Reads up to 'count' bytes from 'ras' starting at position 'pos'
// and verifies that the read bytes are equal to the corresponding
// subsequence in 'full_contents'.
Status ReadAndVerifyFragment(RandomAccessStream* ras, int pos, int count,
absl::string_view full_contents) {
auto buf_result = util::Buffer::New(count);
if (!buf_result.ok()) {
return Status(crypto::tink::util::error::INTERNAL,
absl::StrCat("Could not allocate buffer of size ", count));
}
auto buf = std::move(buf_result.ValueOrDie());
int full_size = full_contents.size();
auto status = ras->PRead(pos, count, buf.get());
if (!status.ok() && status.error_code() != util::error::OUT_OF_RANGE) {
return Status(
crypto::tink::util::error::INTERNAL,
absl::StrCat("PRead failed with status: ", status.ToString()));
}
int exp_size = std::min(count, full_size - pos);
if (exp_size != buf->size()) {
return Status(crypto::tink::util::error::INTERNAL,
absl::StrCat("PRead returned ", buf->size(), " bytes, while ",
exp_size, " bytes were expected."));
}
if (std::memcmp(full_contents.data() + pos, buf->get_mem_block(), exp_size)) {
return Status(
crypto::tink::util::error::INTERNAL,
absl::StrCat("PRead returned bytes [",
std::string(buf->get_mem_block(), exp_size), "] while [",
full_contents.substr(pos, exp_size), "] were expected."));
}
return Status::OK;
}
} // namespace
crypto::tink::util::Status EncryptThenDecrypt(StreamingAead* encrypter,
StreamingAead* decrypter,
absl::string_view plaintext,
absl::string_view associated_data,
int ciphertext_offset) {
// Prepare ciphertext destination stream.
auto ct_stream = absl::make_unique<std::stringstream>();
// A reference to the ciphertext buffer, for later validation.
auto ct_buf = ct_stream->rdbuf();
auto ct_destination =
absl::make_unique<OstreamOutputStream>(std::move(ct_stream));
auto status = subtle::test::WriteToStream(
ct_destination.get(), std::string(ciphertext_offset, 'o'), false);
if (!status.ok()) return status;
// Use encrypter to encrypt some data.
auto enc_stream_result = encrypter->NewEncryptingStream(
std::move(ct_destination), associated_data);
if (!enc_stream_result.ok()) return enc_stream_result.status();
auto enc_stream = std::move(enc_stream_result.ValueOrDie());
status = subtle::test::WriteToStream(enc_stream.get(), plaintext);
if (!status.ok()) return status;
if (plaintext.size() != enc_stream->Position()) {
return ::crypto::tink::util::Status(
crypto::tink::util::error::INTERNAL,
"Plaintext size different from stream position.");
}
// Prepare an InputStream with the ciphertext.
auto ct_bytes = absl::make_unique<std::stringstream>(
ct_buf->str().substr(ciphertext_offset));
std::unique_ptr<InputStream> ct_source(
absl::make_unique<IstreamInputStream>(std::move(ct_bytes)));
// Decrypt the ciphertext using the decrypter.
auto dec_stream_result = decrypter->NewDecryptingStream(
std::move(ct_source), associated_data);
if (!dec_stream_result.ok()) return dec_stream_result.status();
auto dec_stream = std::move(dec_stream_result.ValueOrDie());
std::string decrypted;
status = subtle::test::ReadFromStream(dec_stream.get(), &decrypted);
if (!status.ok()) {
return status;
}
if (plaintext != decrypted) {
return ::crypto::tink::util::Status(crypto::tink::util::error::INTERNAL,
"Decryption differs from plaintext.");
}
// Prepare a RandomAccessStream with the ciphertext.
auto ct_ras = GetRandomAccessStreamContaining(std::string(ct_buf->str()));
// Decrypt fragments of the ciphertext using the decrypter.
auto dec_ras_result = decrypter->NewDecryptingRandomAccessStream(
std::move(ct_ras), associated_data);
if (!dec_ras_result.ok()) return dec_ras_result.status();
auto dec_ras = std::move(dec_ras_result.ValueOrDie());
int pt_size = plaintext.size();
for (int pos : {0, pt_size / 2, std::max(pt_size - 10, 0)}) {
for (int count : {1, 10, std::max(pt_size / 2, 1), std::max(pt_size, 1)}) {
auto status = ReadAndVerifyFragment(dec_ras.get(), pos, count, plaintext);
if (!status.ok()) {
return Status(
crypto::tink::util::error::INTERNAL,
absl::StrCat("Random access decryption failed at position=", pos,
" with count=", count,
" and status: ", status.ToString()));
}
}
}
return crypto::tink::util::OkStatus();
}
} // namespace tink
} // namespace crypto