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