| // Copyright 2018 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "tink/subtle/xchacha20_poly1305_boringssl.h" |
| |
| #include <cstdint> |
| #include <string> |
| #include <vector> |
| |
| #include "openssl/err.h" |
| #include "openssl/evp.h" |
| #include "tink/aead.h" |
| #include "tink/config/tink_fips.h" |
| #include "tink/subtle/random.h" |
| #include "tink/subtle/subtle_util.h" |
| #include "tink/subtle/subtle_util_boringssl.h" |
| #include "tink/util/errors.h" |
| #include "tink/util/status.h" |
| #include "tink/util/statusor.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace subtle { |
| |
| namespace { |
| const bool IsValidKeySize(uint32_t size_in_bytes) { |
| return size_in_bytes == 32; |
| } |
| } // namespace |
| |
| util::StatusOr<std::unique_ptr<Aead>> XChacha20Poly1305BoringSsl::New( |
| util::SecretData key) { |
| auto status = CheckFipsCompatibility<XChacha20Poly1305BoringSsl>(); |
| if (!status.ok()) return status; |
| |
| if (!IsValidKeySize(key.size())) { |
| return util::Status(util::error::INVALID_ARGUMENT, "Invalid key size"); |
| } |
| |
| const EVP_AEAD* cipher = EVP_aead_xchacha20_poly1305(); |
| if (cipher == nullptr) { |
| return util::Status(util::error::INTERNAL, "Failed to get EVP_AEAD"); |
| } |
| return std::unique_ptr<Aead>( |
| new XChacha20Poly1305BoringSsl(std::move(key), cipher)); |
| } |
| |
| util::StatusOr<std::string> XChacha20Poly1305BoringSsl::Encrypt( |
| absl::string_view plaintext, absl::string_view additional_data) const { |
| bssl::UniquePtr<EVP_AEAD_CTX> ctx( |
| EVP_AEAD_CTX_new(aead_, reinterpret_cast<const uint8_t*>(key_.data()), |
| key_.size(), kTagSize)); |
| if (ctx.get() == nullptr) { |
| return util::Status(util::error::INTERNAL, |
| "could not initialize EVP_AEAD_CTX"); |
| } |
| |
| // BoringSSL expects a non-null pointer for plaintext and additional_data, |
| // regardless of whether the size is 0. |
| plaintext = SubtleUtilBoringSSL::EnsureNonNull(plaintext); |
| additional_data = SubtleUtilBoringSSL::EnsureNonNull(additional_data); |
| |
| const std::string nonce = Random::GetRandomBytes(kNonceSize); |
| if (nonce.size() != kNonceSize) { |
| return util::Status(util::error::INTERNAL, |
| "Failed to get enough random bytes for nonce"); |
| } |
| |
| size_t ciphertext_size = nonce.size() + plaintext.size() + kTagSize; |
| |
| // Write the nonce in the output buffer. |
| std::string ct = nonce; |
| ResizeStringUninitialized(&ct, ciphertext_size); |
| size_t written = nonce.size(); |
| |
| // Encrypt the plaintext and store it after the nonce. |
| size_t out_len = 0; |
| int ret = EVP_AEAD_CTX_seal( |
| ctx.get(), reinterpret_cast<uint8_t*>(&ct[written]), &out_len, |
| ciphertext_size - written, reinterpret_cast<const uint8_t*>(nonce.data()), |
| nonce.size(), reinterpret_cast<const uint8_t*>(plaintext.data()), |
| plaintext.size(), |
| reinterpret_cast<const uint8_t*>(additional_data.data()), |
| additional_data.size()); |
| if (ret != 1) { |
| return util::Status(util::error::INTERNAL, "EVP_AEAD_CTX_seal failed"); |
| } |
| written += out_len; |
| |
| // Verify that all the expected data has been written. |
| if (written != ciphertext_size) { |
| return util::Status(util::error::INTERNAL, "Incorrect ciphertext size"); |
| } |
| return ct; |
| } |
| |
| util::StatusOr<std::string> XChacha20Poly1305BoringSsl::Decrypt( |
| absl::string_view ciphertext, absl::string_view additional_data) const { |
| // BoringSSL expects a non-null pointer for additional_data, |
| // regardless of whether the size is 0. |
| additional_data = SubtleUtilBoringSSL::EnsureNonNull(additional_data); |
| |
| if (ciphertext.size() < kNonceSize + kTagSize) { |
| return util::Status(util::error::INVALID_ARGUMENT, "Ciphertext too short"); |
| } |
| |
| bssl::UniquePtr<EVP_AEAD_CTX> ctx( |
| EVP_AEAD_CTX_new(aead_, reinterpret_cast<const uint8_t*>(key_.data()), |
| key_.size(), kTagSize)); |
| if (ctx.get() == nullptr) { |
| return util::Status(util::error::INTERNAL, |
| "could not initialize EVP_AEAD_CTX"); |
| } |
| |
| std::string out; |
| size_t out_size = ciphertext.size() - kNonceSize - kTagSize; |
| ResizeStringUninitialized(&out, out_size); |
| |
| absl::string_view nonce = ciphertext.substr(0, kNonceSize); |
| absl::string_view encrypted = |
| ciphertext.substr(kNonceSize, out_size + kTagSize); |
| |
| size_t len = 0; |
| int ret = EVP_AEAD_CTX_open( |
| ctx.get(), reinterpret_cast<uint8_t*>(&out[0]), &len, out_size, |
| reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(), |
| reinterpret_cast<const uint8_t*>(encrypted.data()), encrypted.size(), |
| reinterpret_cast<const uint8_t*>(additional_data.data()), |
| additional_data.size()); |
| if (ret != 1) { |
| return util::Status(util::error::INTERNAL, "EVP_AEAD_CTX_open failed"); |
| } |
| |
| if (len != out_size) { |
| return util::Status(util::error::INTERNAL, "Incorrect output size"); |
| } |
| |
| return out; |
| } |
| |
| } // namespace subtle |
| } // namespace tink |
| } // namespace crypto |