Asylo
aes_gcm_siv.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2017 Asylo authors
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef ASYLO_CRYPTO_AES_GCM_SIV_H_
20 #define ASYLO_CRYPTO_AES_GCM_SIV_H_
21 
22 #include <openssl/evp.h>
23 #include <openssl/sha.h>
24 #include <memory>
25 #include <vector>
26 
27 #include "absl/strings/str_cat.h"
28 #include "asylo/crypto/nonce_generator.h"
29 #include "asylo/crypto/util/bssl_util.h"
30 #include "asylo/crypto/util/bytes.h"
31 #include "asylo/util/logging.h"
32 #include "asylo/util/cleansing_types.h"
33 #include "asylo/util/status.h"
34 #include "asylo/util/status_macros.h"
35 #include "asylo/util/statusor.h"
36 
37 namespace asylo {
38 
39 constexpr size_t kAesGcmSivNonceSize = 12;
40 
41 /// A 96-bit NonceGenerator that returns a uniformly distributed random nonce on
42 /// each invocation of NextNonce().
44  public:
46 
47  // Implements NextNonce() from NonceGenerator.
49  AesGcmSivNonce *nonce) override;
50 };
51 
52 /// An AEAD cryptor that provides Seal() and Open() functionality using the AES
53 /// GCM SIV cipher for both 128-bit and 256-bit keys. The class must be
54 /// constructed using a pointer to a 96-bit NonceGenerator. If the
55 /// NonceGenerator is thread-safe, then the constructed object is also
56 /// thread-safe.
57 ///
58 /// The Seal() and Open() methods provided by this cryptor are template methods
59 /// that operate on "byte containers." A byte container is a C++ object that
60 /// meets the following requirements:
61 ///
62 /// * It stores values that are each 1-byte in size.
63 /// * It provides a `size()` method.
64 /// * It provides forward and reverse random-access iterators.
65 /// * It provides `operator[]()` and `at()` accessor methods.
66 /// * It defines `value_type` and `allocator_type` aliases.
67 ///
68 /// A byte container is considered to be resizable if it provides a `resize()`
69 /// method.
70 ///
71 /// A byte container of type T is considered to be self-cleansing if
72 /// `T::allocator_type` is same as `CleansingAllocator<typename T::value_type>`.
73 class AesGcmSivCryptor {
74  public:
75  /// Constructs an AES GCM SIV cryptor that enforces the input
76  /// `message_size_limit` and utilizes `nonce_generator` to generate nonces.
77  ///
78  /// \param message_size_limit Maximum message size supported by this cryptor.
79  /// \param nonce_generator A NonceGenerator that is used by the cryptor for
80  /// generating nonces. The cryptor takes ownership of
81  /// `nonce_generator`.
82  AesGcmSivCryptor(size_t message_size_limit,
83  NonceGenerator<kAesGcmSivNonceSize> *nonce_generator)
86  ~AesGcmSivCryptor() = default;
87 
88  /// Implements AEAD Authenticated Encryption (a.k.a.\ seal) functionality.
89  ///
90  /// \param key The encryption key used by the cryptor. `key` must be a
91  /// container with 1-byte `value_type`.
92  /// \param additional_data Authenticated data for the seal operation.
93  /// `additional_data` must be a container with 1-byte `value_type`.
94  /// \param plaintext The plaintext to be encrypted. `plaintext` must be a
95  /// container with 1-byte `value_type`.
96  /// \param[out] nonce Nonce used in this sealing operation. The cryptor
97  /// samples the nonce value from NonceGenerator it was created
98  /// with. `nonce` must be a pointer to a resizable container with
99  /// 1-byte `value_type`.
100  /// \param[out] ciphertext The ciphertext generated by the
101  /// authenticated-encryption operation. `ciphertext` must be a
102  /// resizable container with 1-byte `value_type`.
103  /// \return A non-OK Status if an error is encountered.
104  template <typename ContainerT, typename ContainerU, typename ContainerV,
105  typename ContainerW, typename ContainerX>
109  static_assert(
110  sizeof(typename ContainerT::value_type) == 1,
111  "Template parameter ContainerT is not a valid byte container");
112  static_assert(
113  sizeof(typename ContainerU::value_type) == 1,
114  "Template parameter ContainerU is not a valid byte container");
115  static_assert(
116  sizeof(typename ContainerV::value_type) == 1,
117  "Template parameter ContainerV is not a valid byte container");
118  static_assert(
119  sizeof(typename ContainerW::value_type) == 1,
120  "Template parameter ContainerW is not a valid byte container");
121  static_assert(
122  sizeof(typename ContainerX::value_type) == 1,
123  "Template parameter ContainerX is not a valid byte container");
124 
125  // Pick the appropriate EVP_AEAD based on the key length.
126  EVP_AEAD const *aead;
127  ASYLO_ASSIGN_OR_RETURN(aead, EvpAead(key.size()));
128 
129  if (additional_data.size() + plaintext.size() > message_size_limit_) {
130  return Status(error::GoogleError::INVALID_ARGUMENT,
131  "Message size is too large");
132  }
133 
134  if (nonce_generator_->nonce_size() != EVP_AEAD_nonce_length(aead)) {
135  return Status(error::GoogleError::INVALID_ARGUMENT,
136  "NonceGenerator produces nonces of incorrect length");
137  }
138 
139  // Get the next nonce from the nonce generator into a local variable, and
140  // also copy it to the output. This function maintains its own copy of the
141  // nonce so that an entity outside this function would not be able to
142  // change the value of the nonce while it is being used.
143  UnsafeBytes<kAesGcmSivNonceSize> nonce_copy;
144  std::vector<uint8_t> key_id(SHA256_DIGEST_LENGTH);
145  if (nonce_generator_->uses_key_id()) {
146  SHA256(reinterpret_cast<const uint8_t *>(key.data()), key.size(),
147  key_id.data());
148  }
149  ASYLO_RETURN_IF_ERROR(nonce_generator_->NextNonce(key_id, &nonce_copy));
150  nonce->resize(nonce_copy.size());
151 
152  // Since the Bytes template class provides a fake resize method that does
153  // not actually change the size of the container, make sure that *|nonce|
154  // actually has the correct size.
155  if (nonce->size() != nonce_copy.size()) {
156  return Status(error::GoogleError::INVALID_ARGUMENT,
157  "Could not resize *|nonce| to correct size");
158  }
159  std::copy(nonce_copy.cbegin(), nonce_copy.cend(), nonce->begin());
160 
161  size_t max_ciphertext_length =
162  plaintext.size() + EVP_AEAD_max_overhead(aead);
163 
164  // Create temporary storage for generating the ciphertext.
165  std::vector<uint8_t> tmp_ciphertext(max_ciphertext_length);
166 
167  // Initialize the AEAD context.
168  EVP_AEAD_CTX context;
169  if (EVP_AEAD_CTX_init(
170  &context, aead, reinterpret_cast<const uint8_t *>(key.data()),
171  key.size(), EVP_AEAD_max_tag_len(aead), nullptr) != 1) {
172  return Status(
173  error::GoogleError::INTERNAL,
174  absl::StrCat("EVP_AEAD_CTX_init failed: ", BsslLastErrorString()));
175  }
176 
177  // Perform actual encryption.
178  size_t ciphertext_length = 0;
179  if (EVP_AEAD_CTX_seal(
180  &context, tmp_ciphertext.data(), &ciphertext_length,
181  tmp_ciphertext.size(), nonce_copy.data(), nonce_copy.size(),
182  reinterpret_cast<const uint8_t *>(plaintext.data()),
183  plaintext.size(),
184  reinterpret_cast<const uint8_t *>(additional_data.data()),
185  additional_data.size()) != 1) {
186  EVP_AEAD_CTX_cleanup(&context);
187  return Status(
188  error::GoogleError::INTERNAL,
189  absl::StrCat("EVP_AEAD_CTX_seal failed: ", BsslLastErrorString()));
190  }
191 
192  // Resize and copy the output.
193  tmp_ciphertext.resize(ciphertext_length);
194  ciphertext->resize(ciphertext_length);
195 
196  // Since the Bytes template class provides a fake resize method that does
197  // not actually change the size of the container, make sure that
198  // *|ciphertext| actually has the correct size.
199  if (ciphertext->size() != ciphertext_length) {
200  EVP_AEAD_CTX_cleanup(&context);
201  return Status(error::GoogleError::INVALID_ARGUMENT,
202  "Could not resize *|ciphertext| to correct size");
203  }
204  std::copy(tmp_ciphertext.cbegin(), tmp_ciphertext.cend(),
205  ciphertext->begin());
206 
207  EVP_AEAD_CTX_cleanup(&context);
208  return Status::OkStatus();
209  }
210 
211  /// Implements AEAD Authenticated Decryption (a.k.a.\ open) functionality.
212  ///
213  /// \param key The encryption key used by the cryptor. `key` must be a
214  /// container with 1-byte `value_type`.
215  /// \param additional_data Authenticated data for the open operation.
216  /// `additional_data` must be a container with 1-byte `value_type`.
217  /// \param ciphertext The ciphertext to be decrypted. `ciphertext` must
218  /// be a container with 1-byte `value_type`.
219  /// \param nonce Nonce used in this open operation. `nonce` must be a
220  /// container with 1-byte `value_type`.
221  /// \param[out] plaintext The plaintext generated by the
222  /// authenticated-decryption operation. `plaintext` must be a
223  /// resizable, self-cleansing container with 1-byte `value_type`.
224  /// \return A non-OK Status if error encountered.
225  template <typename ContainerT, typename ContainerU, typename ContainerV,
226  typename ContainerW, typename ContainerX>
228  const ContainerV &ciphertext, const ContainerW &nonce,
230  static_assert(
231  sizeof(typename ContainerT::value_type) == 1,
232  "Template parameter ContainerT is not a valid byte container");
233  static_assert(
234  sizeof(typename ContainerU::value_type) == 1,
235  "Template parameter ContainerU is not a valid byte container");
236  static_assert(
237  sizeof(typename ContainerV::value_type) == 1,
238  "Template parameter ContainerV is not a valid byte container");
239  static_assert(
240  sizeof(typename ContainerW::value_type) == 1,
241  "Template parameter ContainerW is not a valid byte container");
242  static_assert(
243  sizeof(typename ContainerX::value_type) == 1,
244  "Template parameter ContainerX is not a valid byte container");
245  using PlaintextContainerT =
246  typename std::remove_reference<decltype(*plaintext)>::type;
247  using PlaintextValueT = typename PlaintextContainerT::value_type;
248  static_assert(std::is_same<typename PlaintextContainerT::allocator_type,
249  CleansingAllocator<PlaintextValueT>>::value,
250  "Ciphertext container must be self-cleansing");
251 
252  // Pick the appropriate EVP_AEAD based on the key length.
253  EVP_AEAD const *aead;
254  ASYLO_ASSIGN_OR_RETURN(aead, EvpAead(key.size()));
255 
256  if (nonce.size() != EVP_AEAD_nonce_length(aead)) {
257  return Status(error::GoogleError::INVALID_ARGUMENT,
258  "|nonce| has incorrect length");
259  }
260 
261  // Copy the supplied nonce into a local variable. This function maintains
262  // its own copy of the nonce so that an entity outside this function would
263  // not be able to change the value of the nonce while it is being used.
264  std::vector<uint8_t> nonce_copy;
265  nonce_copy.resize(nonce.size());
266  std::copy(nonce.cbegin(), nonce.cend(), nonce_copy.begin());
267 
268  // Allocate temporary storage for the plaintext. Since the plaintext is
269  // sensitive, the temporary storage consists of a self-cleansing vector to
270  // facilitate RAII-style cleansing when exiting the function.
271  CleansingVector<uint8_t> tmp_plaintext;
272  tmp_plaintext.resize(ciphertext.size());
273 
274  // Initialize the AEAD context.
275  EVP_AEAD_CTX context;
276  if (EVP_AEAD_CTX_init(
277  &context, aead, reinterpret_cast<const uint8_t *>(key.data()),
278  key.size(), EVP_AEAD_max_tag_len(aead), nullptr) != 1) {
279  return Status(
280  error::GoogleError::INTERNAL,
281  absl::StrCat("EVP_AEAD_CTX_init failed: ", BsslLastErrorString()));
282  }
283 
284  // Perform the actual decryption.
285  size_t plaintext_length = 0;
286  if (EVP_AEAD_CTX_open(
287  &context, tmp_plaintext.data(), &plaintext_length,
288  tmp_plaintext.size(),
289  reinterpret_cast<const uint8_t *>(nonce.data()), nonce.size(),
290  reinterpret_cast<const uint8_t *>(ciphertext.data()),
291  ciphertext.size(),
292  reinterpret_cast<const uint8_t *>(additional_data.data()),
293  additional_data.size()) != 1) {
294  EVP_AEAD_CTX_cleanup(&context);
295  return Status(
296  error::GoogleError::INTERNAL,
297  absl::StrCat("EVP_AEAD_CTX_open failed: ", BsslLastErrorString()));
298  }
299 
300  // Resize and copy the output.
301  tmp_plaintext.resize(plaintext_length);
302  plaintext->resize(plaintext_length);
303 
304  // Since the Bytes template class provides a fake resize method that does
305  // not actually change the size of the container, make sure that
306  // *|plaintext| actually has the correct size.
307  if (plaintext->size() != plaintext_length) {
308  EVP_AEAD_CTX_cleanup(&context);
309  return Status(error::GoogleError::INVALID_ARGUMENT,
310  "Could not resize *|plaintext| to correct size");
311  }
312  std::copy(tmp_plaintext.cbegin(), tmp_plaintext.cend(), plaintext->begin());
313 
314  EVP_AEAD_CTX_cleanup(&context);
315  return Status::OkStatus();
316  }
317 
318  private:
319  StatusOr<EVP_AEAD const *> EvpAead(size_t key_size) {
320  // Pick the appropriate EVP_AEAD based on the key length.
321  EVP_AEAD const *const aead_128 = EVP_aead_aes_128_gcm_siv();
322  EVP_AEAD const *const aead_256 = EVP_aead_aes_256_gcm_siv();
323 
324  if (key_size == EVP_AEAD_key_length(aead_128)) {
325  return aead_128;
326  } else if (key_size == EVP_AEAD_key_length(aead_256)) {
327  return aead_256;
328  } else {
329  return Status(error::GoogleError::INVALID_ARGUMENT,
330  absl::StrCat("Key size ", key_size, " is invalid"));
331  }
332  }
333 
334  const size_t message_size_limit_;
335  std::unique_ptr<NonceGenerator<kAesGcmSivNonceSize>> nonce_generator_;
336 };
337 
338 } // namespace asylo
339 
340 #endif // ASYLO_CRYPTO_AES_GCM_SIV_H_
constexpr size_t kAesGcmSivNonceSize
Definition: aes_gcm_siv.h:39
AesGcmSivCryptor(size_t message_size_limit, NonceGenerator< kAesGcmSivNonceSize > *nonce_generator)
Constructs an AES GCM SIV cryptor that enforces the input message_size_limit and utilizes nonce_gener...
Definition: aes_gcm_siv.h:82
Status NextNonce(const std::vector< uint8_t > &key_id, AesGcmSivNonce *nonce) override
A 96-bit NonceGenerator that returns a uniformly distributed random nonce on each invocation of NextN...
Definition: aes_gcm_siv.h:43
Status Open(const ContainerT &key, const ContainerU &additional_data, const ContainerV &ciphertext, const ContainerW &nonce, ContainerX *plaintext)
Implements AEAD Authenticated Decryption (a.k.a. open) functionality.
Definition: aes_gcm_siv.h:227
Status Seal(const ContainerT &key, const ContainerU &additional_data, const ContainerV &plaintext, ContainerW *nonce, ContainerX *ciphertext)
Implements AEAD Authenticated Encryption (a.k.a. seal) functionality.
Definition: aes_gcm_siv.h:106
Definition: aes_gcm_siv.h:37