xref: /aosp_15_r20/external/tink/cc/subtle/streaming_aead_test_util.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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