• 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/subtle/aes_gcm_hkdf_stream_segment_decrypter.h"
18 
19 #include <cstdint>
20 #include <cstring>
21 #include <limits>
22 #include <memory>
23 #include <utility>
24 #include <vector>
25 
26 #include "absl/algorithm/container.h"
27 #include "absl/base/config.h"
28 #include "absl/memory/memory.h"
29 #include "absl/status/status.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/string_view.h"
32 #include "absl/types/span.h"
33 #include "tink/aead/internal/ssl_aead.h"
34 #include "tink/internal/err_util.h"
35 #include "tink/subtle/aes_gcm_hkdf_stream_segment_encrypter.h"
36 #include "tink/subtle/common_enums.h"
37 #include "tink/subtle/hkdf.h"
38 #include "tink/subtle/random.h"
39 #include "tink/util/secret_data.h"
40 #include "tink/util/status.h"
41 #include "tink/util/statusor.h"
42 
43 namespace crypto {
44 namespace tink {
45 namespace subtle {
46 
47 namespace {
48 
ByteSwap(uint32_t val)49 uint32_t ByteSwap(uint32_t val) {
50   return ((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) |
51          ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24);
52 }
53 
BigEndianStore32(uint8_t dst[4],uint32_t val)54 void BigEndianStore32(uint8_t dst[4], uint32_t val) {
55 #if defined(ABSL_IS_LITTLE_ENDIAN)
56   val = ByteSwap(val);
57 #elif !defined(ABSL_IS_BIG_ENDIAN)
58 #error Unknown endianness
59 #endif
60   std::memcpy(dst, &val, sizeof(val));
61 }
62 
Validate(const AesGcmHkdfStreamSegmentDecrypter::Params & params)63 util::Status Validate(const AesGcmHkdfStreamSegmentDecrypter::Params& params) {
64   if (!(params.hkdf_hash == SHA1 || params.hkdf_hash == SHA256 ||
65         params.hkdf_hash == SHA512)) {
66     return util::Status(absl::StatusCode::kInvalidArgument,
67                         "unsupported hkdf_hash");
68   }
69   if (params.derived_key_size != 16 && params.derived_key_size != 32) {
70     return util::Status(absl::StatusCode::kInvalidArgument,
71                         "derived_key_size must be 16 or 32");
72   }
73   if (params.ikm.size() < 16 || params.ikm.size() < params.derived_key_size) {
74     return util::Status(absl::StatusCode::kInvalidArgument, "ikm too small");
75   }
76   if (params.ciphertext_offset < 0) {
77     return util::Status(absl::StatusCode::kInvalidArgument,
78                         "ciphertext_offset must be non-negative");
79   }
80   int header_size = 1 + params.derived_key_size +
81                     AesGcmHkdfStreamSegmentEncrypter::kNoncePrefixSizeInBytes;
82   if (params.ciphertext_segment_size <=
83       params.ciphertext_offset + header_size +
84           AesGcmHkdfStreamSegmentEncrypter::kTagSizeInBytes) {
85     return util::Status(absl::StatusCode::kInvalidArgument,
86                         "ciphertext_segment_size too small");
87   }
88   return util::OkStatus();
89 }
90 
91 }  // namespace
92 
AesGcmHkdfStreamSegmentDecrypter(Params params)93 AesGcmHkdfStreamSegmentDecrypter::AesGcmHkdfStreamSegmentDecrypter(
94     Params params)
95     : ikm_(std::move(params.ikm)),
96       hkdf_hash_(params.hkdf_hash),
97       derived_key_size_(params.derived_key_size),
98       ciphertext_offset_(params.ciphertext_offset),
99       ciphertext_segment_size_(params.ciphertext_segment_size),
100       associated_data_(std::move(params.associated_data)),
101       header_size_(1 + derived_key_size_ +
102                    AesGcmHkdfStreamSegmentEncrypter::kNoncePrefixSizeInBytes) {}
103 
104 // static
105 util::StatusOr<std::unique_ptr<StreamSegmentDecrypter>>
New(Params params)106 AesGcmHkdfStreamSegmentDecrypter::New(Params params) {
107   auto status = Validate(params);
108   if (!status.ok()) {
109     return status;
110   }
111   return {absl::WrapUnique(
112       new AesGcmHkdfStreamSegmentDecrypter(std::move(params)))};
113 }
114 
Init(const std::vector<uint8_t> & header)115 util::Status AesGcmHkdfStreamSegmentDecrypter::Init(
116     const std::vector<uint8_t>& header) {
117   if (is_initialized_) {
118     return util::Status(absl::StatusCode::kFailedPrecondition,
119                         "decrypter already initialized");
120   }
121   if (header.size() != header_size_) {
122     return util::Status(
123         absl::StatusCode::kInvalidArgument,
124         absl::StrCat("wrong header size, expected ", header_size_, " bytes"));
125   }
126   if (header[0] != header_size_) {
127     return util::Status(absl::StatusCode::kInvalidArgument, "corrupted header");
128   }
129 
130   // Extract salt and nonce_prefix.
131   salt_.resize(derived_key_size_);
132   nonce_prefix_.resize(
133       AesGcmHkdfStreamSegmentEncrypter::kNoncePrefixSizeInBytes);
134   absl::c_copy(absl::MakeSpan(header).subspan(1, derived_key_size_),
135                salt_.begin());
136   absl::c_copy(absl::MakeSpan(header).subspan(
137                    1 + derived_key_size_,
138                    AesGcmHkdfStreamSegmentEncrypter::kNoncePrefixSizeInBytes),
139                nonce_prefix_.begin());
140 
141   // Derive symmetric key.
142   util::StatusOr<util::SecretData> key = Hkdf::ComputeHkdf(
143       hkdf_hash_, ikm_,
144       absl::string_view(reinterpret_cast<const char*>(salt_.data()),
145                         derived_key_size_),
146       associated_data_, derived_key_size_);
147   if (!key.ok()) {
148     return key.status();
149   }
150 
151   util::StatusOr<std::unique_ptr<internal::SslOneShotAead>> aead_ptr =
152       internal::CreateAesGcmOneShotCrypter(*key);
153   if (!aead_ptr.ok()) {
154     return aead_ptr.status();
155   }
156   aead_ = *std::move(aead_ptr);
157   is_initialized_ = true;
158   return util::OkStatus();
159 }
160 
get_plaintext_segment_size() const161 int AesGcmHkdfStreamSegmentDecrypter::get_plaintext_segment_size() const {
162   return ciphertext_segment_size_ -
163          AesGcmHkdfStreamSegmentEncrypter::kTagSizeInBytes;
164 }
165 
DecryptSegment(const std::vector<uint8_t> & ciphertext,int64_t segment_number,bool is_last_segment,std::vector<uint8_t> * plaintext_buffer)166 util::Status AesGcmHkdfStreamSegmentDecrypter::DecryptSegment(
167     const std::vector<uint8_t>& ciphertext, int64_t segment_number,
168     bool is_last_segment, std::vector<uint8_t>* plaintext_buffer) {
169   if (!is_initialized_) {
170     return util::Status(absl::StatusCode::kFailedPrecondition,
171                         "decrypter not initialized");
172   }
173   if (ciphertext.size() > get_ciphertext_segment_size()) {
174     return util::Status(absl::StatusCode::kInvalidArgument,
175                         "ciphertext too long");
176   }
177   if (ciphertext.size() < AesGcmHkdfStreamSegmentEncrypter::kTagSizeInBytes) {
178     return util::Status(absl::StatusCode::kInvalidArgument,
179                         "ciphertext too short");
180   }
181   if (plaintext_buffer == nullptr) {
182     return util::Status(absl::StatusCode::kInvalidArgument,
183                         "plaintext_buffer must be non-null");
184   }
185   if (segment_number > std::numeric_limits<uint32_t>::max() ||
186       (segment_number == std::numeric_limits<uint32_t>::max() &&
187        !is_last_segment)) {
188     return util::Status(absl::StatusCode::kInvalidArgument,
189                         "too many segments");
190   }
191 
192   const int64_t kPlaintextSize =
193       ciphertext.size() - AesGcmHkdfStreamSegmentEncrypter::kTagSizeInBytes;
194   plaintext_buffer->resize(kPlaintextSize);
195 
196   // Construct IV.
197   std::vector<uint8_t> iv(AesGcmHkdfStreamSegmentEncrypter::kNonceSizeInBytes);
198   absl::c_copy(nonce_prefix_, iv.begin());
199   BigEndianStore32(
200       iv.data() + AesGcmHkdfStreamSegmentEncrypter::kNoncePrefixSizeInBytes,
201       static_cast<uint32_t>(segment_number));
202   iv.back() = is_last_segment ? 1 : 0;
203 
204   util::StatusOr<uint64_t> written_bytes = aead_->Decrypt(
205       absl::string_view(reinterpret_cast<const char*>(ciphertext.data()),
206                         ciphertext.size()),
207       /*associated_data=*/absl::string_view(""),
208       absl::string_view(reinterpret_cast<const char*>(iv.data()), iv.size()),
209       absl::Span<char>(reinterpret_cast<char*>(plaintext_buffer->data()),
210                        plaintext_buffer->size()));
211   if (!written_bytes.ok()) {
212     return written_bytes.status();
213   }
214   return util::OkStatus();
215 }
216 
217 }  // namespace subtle
218 }  // namespace tink
219 }  // namespace crypto
220