xref: /aosp_15_r20/external/tink/testing/cc/streaming_aead_impl.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2020 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 
17 // Implementation of a StreamingAEAD Service.
18 #include "streaming_aead_impl.h"
19 
20 #include <algorithm>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 
25 #include "absl/status/status.h"
26 #include "tink/streaming_aead.h"
27 #include "tink/util/istream_input_stream.h"
28 #include "tink/util/ostream_output_stream.h"
29 #include "tink/util/status.h"
30 #include "create.h"
31 #include "proto/testing_api.grpc.pb.h"
32 
33 namespace tink_testing_api {
34 
35 using ::crypto::tink::InputStream;
36 using ::crypto::tink::util::IstreamInputStream;
37 using ::crypto::tink::util::OstreamOutputStream;
38 using ::crypto::tink::util::StatusOr;
39 
Create(grpc::ServerContext * context,const CreationRequest * request,CreationResponse * response)40 ::grpc::Status StreamingAeadImpl::Create(grpc::ServerContext* context,
41                                          const CreationRequest* request,
42                                          CreationResponse* response) {
43   return CreatePrimitiveForRpc<crypto::tink::StreamingAead>(request, response);
44 }
45 
46 // Encrypts a message
Encrypt(grpc::ServerContext * context,const StreamingAeadEncryptRequest * request,StreamingAeadEncryptResponse * response)47 ::grpc::Status StreamingAeadImpl::Encrypt(
48     grpc::ServerContext* context,
49     const StreamingAeadEncryptRequest* request,
50     StreamingAeadEncryptResponse* response) {
51   StatusOr<std::unique_ptr<crypto::tink::StreamingAead>> streaming_aead_result =
52       PrimitiveFromSerializedBinaryProtoKeyset<crypto::tink::StreamingAead>(
53           request->annotated_keyset());
54   if (!streaming_aead_result.ok()) {
55     response->set_err(std::string(streaming_aead_result.status().message()));
56     return ::grpc::Status::OK;
57   }
58 
59   auto ciphertext_stream = absl::make_unique<std::stringstream>();
60   auto ciphertext_buf = ciphertext_stream->rdbuf();
61   auto ciphertext_destination(
62       absl::make_unique<OstreamOutputStream>(std::move(ciphertext_stream)));
63 
64   auto encrypting_stream_result =
65       streaming_aead_result.value()->NewEncryptingStream(
66           std::move(ciphertext_destination), request->associated_data());
67   if (!encrypting_stream_result.ok()) {
68     response->set_err(std::string(encrypting_stream_result.status().message()));
69     return ::grpc::Status::OK;
70   }
71   auto encrypting_stream = std::move(encrypting_stream_result.value());
72 
73   auto contents = request->plaintext();
74   void* buffer;
75   int pos = 0;
76   int remaining = contents.length();
77   int available_space = 0;
78   int available_bytes = 0;
79   while (remaining > 0) {
80     auto next_result = encrypting_stream->Next(&buffer);
81     if (!next_result.ok()) {
82       response->set_err(std::string(next_result.status().message()));
83       return ::grpc::Status::OK;
84     }
85     available_space = next_result.value();
86     available_bytes = std::min(available_space, remaining);
87     memcpy(buffer, contents.data() + pos, available_bytes);
88     remaining -= available_bytes;
89     pos += available_bytes;
90   }
91   if (available_space > available_bytes) {
92     encrypting_stream->BackUp(available_space - available_bytes);
93   }
94   auto close_status = encrypting_stream->Close();
95   if (!close_status.ok()) {
96     response->set_err(std::string(close_status.message()));
97     return ::grpc::Status::OK;
98   }
99 
100   response->set_ciphertext(ciphertext_buf->str());
101   return ::grpc::Status::OK;
102 }
103 
104 // Decrypts a ciphertext
Decrypt(grpc::ServerContext * context,const StreamingAeadDecryptRequest * request,StreamingAeadDecryptResponse * response)105 ::grpc::Status StreamingAeadImpl::Decrypt(
106     grpc::ServerContext* context,
107     const StreamingAeadDecryptRequest* request,
108     StreamingAeadDecryptResponse* response) {
109   StatusOr<std::unique_ptr<crypto::tink::StreamingAead>> streaming_aead_result =
110       PrimitiveFromSerializedBinaryProtoKeyset<crypto::tink::StreamingAead>(
111           request->annotated_keyset());
112   if (!streaming_aead_result.ok()) {
113     response->set_err(std::string(streaming_aead_result.status().message()));
114     return ::grpc::Status::OK;
115   }
116 
117   auto ciphertext_stream =
118       absl::make_unique<std::stringstream>(request->ciphertext());
119   std::unique_ptr<InputStream> ciphertext_source(
120       absl::make_unique<IstreamInputStream>(std::move(ciphertext_stream)));
121 
122   auto decrypting_stream_result =
123       streaming_aead_result.value()->NewDecryptingStream(
124           std::move(ciphertext_source), request->associated_data());
125   if (!decrypting_stream_result.ok()) {
126     response->set_err(std::string(decrypting_stream_result.status().message()));
127     return ::grpc::Status::OK;
128   }
129   auto decrypting_stream = std::move(decrypting_stream_result.value());
130 
131   std::string plaintext;
132   const void* buffer;
133   while (true) {
134     auto next_result = decrypting_stream->Next(&buffer);
135     if (next_result.status().code() == absl::StatusCode::kOutOfRange) {
136       // End of stream.
137       break;
138     }
139     if (!next_result.ok()) {
140       response->set_err(std::string(next_result.status().message()));
141       return ::grpc::Status::OK;
142     }
143     auto read_bytes = next_result.value();
144     if (read_bytes > 0) {
145       plaintext.append(
146           std::string(reinterpret_cast<const char*>(buffer), read_bytes));
147     }
148   }
149 
150   response->set_plaintext(plaintext);
151   return ::grpc::Status::OK;
152 }
153 
154 }  // namespace tink_testing_api
155