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/streamingaead/decrypting_random_access_stream.h"
18
19 #include <memory>
20 #include <utility>
21 #include <vector>
22
23 #include "absl/memory/memory.h"
24 #include "absl/status/status.h"
25 #include "absl/synchronization/mutex.h"
26 #include "tink/primitive_set.h"
27 #include "tink/random_access_stream.h"
28 #include "tink/streaming_aead.h"
29 #include "tink/streamingaead/shared_random_access_stream.h"
30 #include "tink/util/buffer.h"
31 #include "tink/util/errors.h"
32 #include "tink/util/status.h"
33 #include "tink/util/statusor.h"
34
35 namespace crypto {
36 namespace tink {
37 namespace streamingaead {
38
39 using crypto::tink::PrimitiveSet;
40 using crypto::tink::StreamingAead;
41 using util::Status;
42 using util::StatusOr;
43
44 using StreamingAeadEntry = PrimitiveSet<StreamingAead>::Entry<StreamingAead>;
45
46 // static
New(std::shared_ptr<PrimitiveSet<StreamingAead>> primitives,std::unique_ptr<crypto::tink::RandomAccessStream> ciphertext_source,absl::string_view associated_data)47 StatusOr<std::unique_ptr<RandomAccessStream>> DecryptingRandomAccessStream::New(
48 std::shared_ptr<PrimitiveSet<StreamingAead>> primitives,
49 std::unique_ptr<crypto::tink::RandomAccessStream> ciphertext_source,
50 absl::string_view associated_data) {
51 if (primitives == nullptr) {
52 return Status(absl::StatusCode::kInvalidArgument,
53 "primitives must be non-null.");
54 }
55 if (ciphertext_source == nullptr) {
56 return Status(absl::StatusCode::kInvalidArgument,
57 "ciphertext_source must be non-null.");
58 }
59 return {absl::WrapUnique(new DecryptingRandomAccessStream(
60 primitives, std::move(ciphertext_source), associated_data))};
61 }
62
PRead(int64_t position,int count,crypto::tink::util::Buffer * dest_buffer)63 util::Status DecryptingRandomAccessStream::PRead(
64 int64_t position, int count,
65 crypto::tink::util::Buffer* dest_buffer) {
66 { // "fast-track": quickly proceed if matching has been attempted/found.
67 if (dest_buffer == nullptr) {
68 return util::Status(absl::StatusCode::kInvalidArgument,
69 "dest_buffer must be non-null");
70 }
71 if (count < 0) {
72 return util::Status(absl::StatusCode::kInvalidArgument,
73 "count cannot be negative");
74 }
75 if (count > dest_buffer->allocated_size()) {
76 return util::Status(absl::StatusCode::kInvalidArgument,
77 "buffer too small");
78 }
79 if (position < 0) {
80 return util::Status(absl::StatusCode::kInvalidArgument,
81 "position cannot be negative");
82 }
83 absl::ReaderMutexLock lock(&matching_mutex_);
84 if (matching_stream_ != nullptr) {
85 return matching_stream_->PRead(position, count, dest_buffer);
86 }
87 if (attempted_matching_) {
88 return Status(absl::StatusCode::kInvalidArgument,
89 "Did not find a decrypter matching the ciphertext stream.");
90 }
91 }
92 // Matching has not been attempted yet, so try it now.
93 absl::MutexLock lock(&matching_mutex_);
94
95 // Re-check that matching hasn't been attempted in the meantime.
96 if (matching_stream_ != nullptr) {
97 return matching_stream_->PRead(position, count, dest_buffer);
98 }
99 if (attempted_matching_) {
100 return Status(absl::StatusCode::kInvalidArgument,
101 "Did not find a decrypter matching the ciphertext stream.");
102 }
103 attempted_matching_ = true;
104 std::vector<StreamingAeadEntry*> all_primitives = primitives_->get_all();
105 for (const StreamingAeadEntry* entry : all_primitives) {
106 StreamingAead& streaming_aead = entry->get_primitive();
107 auto shared_ct = absl::make_unique<SharedRandomAccessStream>(
108 ciphertext_source_.get());
109 auto decrypting_stream_result =
110 streaming_aead.NewDecryptingRandomAccessStream(
111 std::move(shared_ct), associated_data_);
112 if (decrypting_stream_result.ok()) {
113 auto status =
114 decrypting_stream_result.value()->PRead(position, count, dest_buffer);
115 if (status.ok() || status.code() == absl::StatusCode::kOutOfRange) {
116 // Found a match.
117 matching_stream_ = std::move(decrypting_stream_result.value());
118 return status;
119 }
120 }
121 // Not a match, try the next primitive.
122 }
123 return Status(absl::StatusCode::kInvalidArgument,
124 "Could not find a decrypter matching the ciphertext stream.");
125 }
126
size()127 StatusOr<int64_t> DecryptingRandomAccessStream::size() {
128 absl::ReaderMutexLock lock(&matching_mutex_);
129 if (matching_stream_ != nullptr) {
130 return matching_stream_->size();
131 }
132 // TODO(b/139722894): attempt matching here?
133 return Status(absl::StatusCode::kUnavailable, "no matching found yet");
134 }
135
136 } // namespace streamingaead
137 } // namespace tink
138 } // namespace crypto
139