1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 #include "tink/subtle/streaming_aead_test_util.h"
17
18 #include <algorithm>
19 #include <cstring>
20 #include <memory>
21 #include <sstream>
22 #include <string>
23 #include <utility>
24
25 #include "tink/internal/test_random_access_stream.h"
26 #include "tink/random_access_stream.h"
27 #include "tink/subtle/test_util.h"
28 #include "tink/util/buffer.h"
29 #include "tink/util/istream_input_stream.h"
30 #include "tink/util/ostream_output_stream.h"
31 #include "tink/util/status.h"
32
33 namespace crypto {
34 namespace tink {
35
36 using ::crypto::tink::internal::TestRandomAccessStream;
37 using ::crypto::tink::util::IstreamInputStream;
38 using ::crypto::tink::util::OstreamOutputStream;
39 using ::crypto::tink::util::Status;
40
41 namespace {
42
43 // Reads up to 'count' bytes from 'ras' starting at position 'pos'
44 // and verifies that the read bytes are equal to the corresponding
45 // subsequence in 'full_contents'.
ReadAndVerifyFragment(RandomAccessStream * ras,int pos,int count,absl::string_view full_contents)46 Status ReadAndVerifyFragment(RandomAccessStream* ras, int pos, int count,
47 absl::string_view full_contents) {
48 auto buf_result = util::Buffer::New(count);
49 if (!buf_result.ok()) {
50 return Status(absl::StatusCode::kInternal,
51 absl::StrCat("Could not allocate buffer of size ", count));
52 }
53 auto buf = std::move(buf_result.value());
54 int full_size = full_contents.size();
55 auto status = ras->PRead(pos, count, buf.get());
56 if (!status.ok() && status.code() != absl::StatusCode::kOutOfRange) {
57 return Status(
58 absl::StatusCode::kInternal,
59 absl::StrCat("PRead failed with status: ", status.ToString()));
60 }
61 int exp_size = std::min(count, full_size - pos);
62 if (exp_size != buf->size()) {
63 return Status(absl::StatusCode::kInternal,
64 absl::StrCat("PRead returned ", buf->size(), " bytes, while ",
65 exp_size, " bytes were expected."));
66 }
67 if (std::memcmp(full_contents.data() + pos, buf->get_mem_block(), exp_size)) {
68 return Status(
69 absl::StatusCode::kInternal,
70 absl::StrCat("PRead returned bytes [",
71 std::string(buf->get_mem_block(), exp_size), "] while [",
72 full_contents.substr(pos, exp_size), "] were expected."));
73 }
74 return util::OkStatus();
75 }
76
77 } // namespace
78
EncryptThenDecrypt(StreamingAead * encrypter,StreamingAead * decrypter,absl::string_view plaintext,absl::string_view associated_data,int ciphertext_offset)79 crypto::tink::util::Status EncryptThenDecrypt(StreamingAead* encrypter,
80 StreamingAead* decrypter,
81 absl::string_view plaintext,
82 absl::string_view associated_data,
83 int ciphertext_offset) {
84 // Prepare ciphertext destination stream.
85 auto ct_stream = absl::make_unique<std::stringstream>();
86
87 // A reference to the ciphertext buffer, for later validation.
88 auto ct_buf = ct_stream->rdbuf();
89 auto ct_destination =
90 absl::make_unique<OstreamOutputStream>(std::move(ct_stream));
91 auto status = subtle::test::WriteToStream(
92 ct_destination.get(), std::string(ciphertext_offset, 'o'), false);
93 if (!status.ok()) return status;
94
95 // Use encrypter to encrypt some data.
96 auto enc_stream_result = encrypter->NewEncryptingStream(
97 std::move(ct_destination), associated_data);
98 if (!enc_stream_result.ok()) return enc_stream_result.status();
99 auto enc_stream = std::move(enc_stream_result.value());
100 status = subtle::test::WriteToStream(enc_stream.get(), plaintext);
101 if (!status.ok()) return status;
102 if (plaintext.size() != enc_stream->Position()) {
103 return ::crypto::tink::util::Status(
104 absl::StatusCode::kInternal,
105 "Plaintext size different from stream position.");
106 }
107
108 // Prepare an InputStream with the ciphertext.
109 auto ct_bytes = absl::make_unique<std::stringstream>(
110 ct_buf->str().substr(ciphertext_offset));
111 std::unique_ptr<InputStream> ct_source(
112 absl::make_unique<IstreamInputStream>(std::move(ct_bytes)));
113
114 // Decrypt the ciphertext using the decrypter.
115 auto dec_stream_result = decrypter->NewDecryptingStream(
116 std::move(ct_source), associated_data);
117 if (!dec_stream_result.ok()) return dec_stream_result.status();
118 auto dec_stream = std::move(dec_stream_result.value());
119 std::string decrypted;
120 status = subtle::test::ReadFromStream(dec_stream.get(), &decrypted);
121 if (!status.ok()) {
122 return status;
123 }
124 if (plaintext != decrypted) {
125 return ::crypto::tink::util::Status(absl::StatusCode::kInternal,
126 "Decryption differs from plaintext.");
127 }
128
129 // Prepare a RandomAccessStream with the ciphertext.
130 auto ct_ras =
131 std::make_unique<TestRandomAccessStream>(std::string(ct_buf->str()));
132
133 // Decrypt fragments of the ciphertext using the decrypter.
134 auto dec_ras_result = decrypter->NewDecryptingRandomAccessStream(
135 std::move(ct_ras), associated_data);
136 if (!dec_ras_result.ok()) return dec_ras_result.status();
137 auto dec_ras = std::move(dec_ras_result.value());
138 int pt_size = plaintext.size();
139 for (int pos : {0, pt_size / 2, std::max(pt_size - 10, 0)}) {
140 for (int count : {1, 10, std::max(pt_size / 2, 1), std::max(pt_size, 1)}) {
141 auto status = ReadAndVerifyFragment(dec_ras.get(), pos, count, plaintext);
142 if (!status.ok()) {
143 return Status(
144 absl::StatusCode::kInternal,
145 absl::StrCat("Random access decryption failed at position=", pos,
146 " with count=", count,
147 " and status: ", status.ToString()));
148 }
149 }
150 }
151 return crypto::tink::util::OkStatus();
152 }
153
154 } // namespace tink
155 } // namespace crypto
156