xref: /aosp_15_r20/external/tink/cc/subtle/streaming_aead_encrypting_stream.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2019 Google Inc.
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 
17 #include "tink/subtle/streaming_aead_encrypting_stream.h"
18 
19 #include <algorithm>
20 #include <cstring>
21 #include <memory>
22 #include <utility>
23 #include <vector>
24 
25 #include "absl/memory/memory.h"
26 #include "absl/status/status.h"
27 #include "tink/output_stream.h"
28 #include "tink/subtle/stream_segment_encrypter.h"
29 #include "tink/util/statusor.h"
30 
31 using crypto::tink::OutputStream;
32 using crypto::tink::util::Status;
33 using crypto::tink::util::StatusOr;
34 
35 namespace crypto {
36 namespace tink {
37 namespace subtle {
38 
39 namespace {
40 
41 // Writes 'contents' to the specified 'output_stream', which must be non-null.
42 // In case of errors returns the first non-OK status of
43 // output_stream->Next()-operation.
44 
WriteToStream(const std::vector<uint8_t> & contents,OutputStream * output_stream)45 util::Status WriteToStream(const std::vector<uint8_t>& contents,
46                            OutputStream* output_stream) {
47   void* buffer;
48   int pos = 0;
49   int remaining = contents.size();
50   int available_space = 0;
51   int available_bytes = 0;
52   while (remaining > 0) {
53     auto next_result = output_stream->Next(&buffer);
54     if (!next_result.ok()) return next_result.status();
55     available_space = next_result.value();
56     available_bytes = std::min(available_space, remaining);
57     memcpy(buffer, contents.data() + pos, available_bytes);
58     remaining -= available_bytes;
59     pos += available_bytes;
60   }
61   if (available_space > available_bytes) {
62     output_stream->BackUp(available_space - available_bytes);
63   }
64   return util::OkStatus();
65 }
66 
67 }  // anonymous namespace
68 
69 // static
New(std::unique_ptr<StreamSegmentEncrypter> segment_encrypter,std::unique_ptr<OutputStream> ciphertext_destination)70 StatusOr<std::unique_ptr<OutputStream>> StreamingAeadEncryptingStream::New(
71     std::unique_ptr<StreamSegmentEncrypter> segment_encrypter,
72     std::unique_ptr<OutputStream> ciphertext_destination) {
73   if (segment_encrypter == nullptr) {
74     return Status(absl::StatusCode::kInvalidArgument,
75                   "segment_encrypter must be non-null");
76   }
77   if (ciphertext_destination == nullptr) {
78     return Status(absl::StatusCode::kInvalidArgument,
79                   "cipertext_destination must be non-null");
80   }
81   std::unique_ptr<StreamingAeadEncryptingStream> enc_stream(
82       new StreamingAeadEncryptingStream());
83   enc_stream->segment_encrypter_ = std::move(segment_encrypter);
84   enc_stream->ct_destination_ = std::move(ciphertext_destination);
85   int first_segment_size =
86       enc_stream->segment_encrypter_->get_plaintext_segment_size() -
87       enc_stream->segment_encrypter_->get_ciphertext_offset() -
88       enc_stream->segment_encrypter_->get_header().size();
89 
90   if (first_segment_size <= 0) {
91     return Status(absl::StatusCode::kInternal,
92                   "Size of the first segment must be greater than 0.");
93   }
94   enc_stream->pt_buffer_.resize(first_segment_size);
95   enc_stream->pt_to_encrypt_.resize(0);
96   enc_stream->position_ = 0;
97   enc_stream->is_first_segment_ = true;
98   enc_stream->count_backedup_ = first_segment_size;
99   enc_stream->pt_buffer_offset_ = 0;
100   enc_stream->status_ = util::OkStatus();
101   return {std::move(enc_stream)};
102 }
103 
Next(void ** data)104 StatusOr<int> StreamingAeadEncryptingStream::Next(void** data) {
105   if (!status_.ok()) return status_;
106 
107   // The first call to Next().
108   if (is_first_segment_) {
109     is_first_segment_ = false;
110     count_backedup_ = 0;
111     status_ =
112         WriteToStream(segment_encrypter_->get_header(), ct_destination_.get());
113     if (!status_.ok()) return status_;
114     *data = pt_buffer_.data();
115     position_ = pt_buffer_.size();
116     return pt_buffer_.size();
117   }
118 
119   // If some space was backed up, return it first.
120   if (count_backedup_ > 0) {
121     position_ += count_backedup_;
122     pt_buffer_offset_ = pt_buffer_.size() - count_backedup_;
123     int backedup = count_backedup_;
124     count_backedup_ = 0;
125     *data = pt_buffer_.data() + pt_buffer_offset_;
126     return backedup;
127   }
128 
129   // We're past the first segment, and no space was backed up, so we:
130   // 1. encrypt pt_to_encrypt_ (if non-empty) as a not-last segment
131   //    and attempt to write the ciphertext to ct_destination_.
132   // 2. move contents of pt_buffer_ to pt_to_encrypt_ (for later encryption,
133   //    as we don't know yet whether it will be the last segment or not.
134   // 3. prepare and return "fresh" pt_buffer_.
135   //
136   // Step 1.
137   if (!pt_to_encrypt_.empty()) {
138     status_ = segment_encrypter_->EncryptSegment(
139         pt_to_encrypt_, /* is_last_segment = */ false, &ct_buffer_);
140     if (!status_.ok()) return status_;
141     status_ = WriteToStream(ct_buffer_, ct_destination_.get());
142     if (!status_.ok()) return status_;
143   }
144   // Step 2.
145   pt_buffer_.swap(pt_to_encrypt_);
146   // Step 3.
147   pt_buffer_.resize(segment_encrypter_->get_plaintext_segment_size());
148   *data = pt_buffer_.data();
149   pt_buffer_offset_ = 0;
150   position_ += pt_buffer_.size();
151   return pt_buffer_.size();
152 }
153 
BackUp(int count)154 void StreamingAeadEncryptingStream::BackUp(int count) {
155   if (is_first_segment_ || !status_.ok() || count < 1) return;
156   int curr_buffer_size = pt_buffer_.size() - pt_buffer_offset_;
157   int actual_count = std::min(count, curr_buffer_size - count_backedup_);
158   count_backedup_ += actual_count;
159   position_ -= actual_count;
160 }
161 
Close()162 Status StreamingAeadEncryptingStream::Close() {
163   if (!status_.ok()) return status_;
164   if (is_first_segment_) {  // Next() was never called.
165     status_ =
166         WriteToStream(segment_encrypter_->get_header(), ct_destination_.get());
167     if (!status_.ok()) return status_;
168   }
169 
170   // The last segment encrypts plaintext from pt_to_encrypt_,
171   // unless the current pt_buffer_ has some plaintext bytes.
172   std::vector<uint8_t>* pt_last_segment = &pt_to_encrypt_;
173   if ((!pt_buffer_.empty()) && count_backedup_ < pt_buffer_.size()) {
174     // The last segment encrypts plaintext from pt_buffer_.
175     pt_buffer_.resize(pt_buffer_.size() - count_backedup_);
176     pt_last_segment = &pt_buffer_;
177   }
178   if (pt_last_segment != &pt_to_encrypt_ && (!pt_to_encrypt_.empty())) {
179     // Before writing the last segment we must encrypt pt_to_encrypt_.
180     status_ = segment_encrypter_->EncryptSegment(
181         pt_to_encrypt_, /* is_last_segment = */ false, &ct_buffer_);
182     if (!status_.ok()) {
183       ct_destination_->Close().IgnoreError();
184       return status_;
185     }
186     status_ = WriteToStream(ct_buffer_, ct_destination_.get());
187     if (!status_.ok()) {
188       ct_destination_->Close().IgnoreError();
189       return status_;
190     }
191   }
192 
193   // Encrypt pt_last_segment, write the ciphertext, and close the stream.
194   status_ = segment_encrypter_->EncryptSegment(
195       *pt_last_segment, /* is_last_segment = */ true, &ct_buffer_);
196   if (!status_.ok()) {
197     ct_destination_->Close().IgnoreError();
198     return status_;
199   }
200   status_ = WriteToStream(ct_buffer_, ct_destination_.get());
201   if (!status_.ok()) {
202     ct_destination_->Close().IgnoreError();
203     return status_;
204   }
205   status_ = Status(absl::StatusCode::kFailedPrecondition, "Stream closed");
206   return ct_destination_->Close();
207 }
208 
Position() const209 int64_t StreamingAeadEncryptingStream::Position() const { return position_; }
210 
211 }  // namespace subtle
212 }  // namespace tink
213 }  // namespace crypto
214