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