// 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 <string> #include <vector> #include "openssl/err.h" #include "openssl/evp.h" #include "tink/aead.h" #include "tink/subtle/random.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 { static const bool IsValidKeySize(uint32_t size_in_bytes) { return size_in_bytes == 32; } XChacha20Poly1305BoringSsl::XChacha20Poly1305BoringSsl( absl::string_view key_value, const EVP_AEAD* aead) : key_(key_value), aead_(aead) {} util::StatusOr<std::unique_ptr<Aead>> XChacha20Poly1305BoringSsl::New( absl::string_view key_value) { if (!IsValidKeySize(key_value.size())) { return util::Status(util::error::INTERNAL, "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"); } std::unique_ptr<Aead> aead(new XChacha20Poly1305BoringSsl(key_value, cipher)); return std::move(aead); } 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(), TAG_SIZE)); 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(NONCE_SIZE); if (nonce.size() != NONCE_SIZE) { return util::Status(util::error::INTERNAL, "Failed to get enough random bytes for nonce"); } size_t ciphertext_size = nonce.size() + plaintext.size() + TAG_SIZE; // Write the nonce in the output buffer. std::vector<uint8_t> ct(ciphertext_size + 1); memcpy(&ct[0], reinterpret_cast<const uint8_t*>(nonce.data()), nonce.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(), &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 std::string(reinterpret_cast<const char*>(&ct[0]), written); } 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() < NONCE_SIZE + TAG_SIZE) { return util::Status(util::error::INTERNAL, "Ciphertext too short"); } bssl::UniquePtr<EVP_AEAD_CTX> ctx( EVP_AEAD_CTX_new(aead_, reinterpret_cast<const uint8_t*>(key_.data()), key_.size(), TAG_SIZE)); if (ctx.get() == nullptr) { return util::Status(util::error::INTERNAL, "could not initialize EVP_AEAD_CTX"); } size_t out_size = ciphertext.size() - NONCE_SIZE - TAG_SIZE; std::vector<uint8_t> out(out_size + 1); absl::string_view nonce = ciphertext.substr(0, NONCE_SIZE); absl::string_view encrypted = ciphertext.substr(NONCE_SIZE, out_size + TAG_SIZE); size_t len = 0; int ret = EVP_AEAD_CTX_open( ctx.get(), &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 std::string(reinterpret_cast<const char*>(&out[0]), out_size); } } // namespace subtle } // namespace tink } // namespace crypto