• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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