// Copyright 2017 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/hkdf.h" #include #include #include "absl/algorithm/container.h" #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "openssl/evp.h" // BoringSSL and OpenSSL have incompatible ways to compute HKDF: BoringSSL // provides a one-shot API HKDF, while OpenSSL doesn't make that API public, but // instead provides this functionality over the EVP interface, which in turn // doesn't provide means to compute HKDF in BoringSSL. As a consequence, we need // to selectively include the correct header and use different implementations. #ifdef OPENSSL_IS_BORINGSSL #include "openssl/base.h" #include "openssl/hkdf.h" #else #include "openssl/kdf.h" #endif #include "tink/internal/md_util.h" #include "tink/internal/ssl_unique_ptr.h" #include "tink/subtle/common_enums.h" #include "tink/subtle/subtle_util.h" #include "tink/util/secret_data.h" #include "tink/util/status.h" #include "tink/util/statusor.h" namespace crypto { namespace tink { namespace subtle { namespace { // Compute HKDF using `evp_md` hashing, key `ikm`, salt `salt` and info `info`. // The result is written to `key`. util::Status SslHkdf(const EVP_MD *evp_md, absl::string_view ikm, absl::string_view salt, absl::string_view info, absl::Span out_key) { const uint8_t *ikm_ptr = reinterpret_cast(ikm.data()); const uint8_t *salt_ptr = reinterpret_cast(salt.data()); const uint8_t *info_ptr = reinterpret_cast(info.data()); #ifdef OPENSSL_IS_BORINGSSL if (HKDF(out_key.data(), out_key.size(), evp_md, ikm_ptr, ikm.size(), salt_ptr, salt.size(), info_ptr, info.size()) != 1) { return util::Status(absl::StatusCode::kInternal, "HKDF failed"); } return util::OkStatus(); #else internal::SslUniquePtr pctx( EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, /*e=*/nullptr)); if (pctx == nullptr || EVP_PKEY_derive_init(pctx.get()) <= 0 || EVP_PKEY_CTX_set_hkdf_md(pctx.get(), evp_md) <= 0 || EVP_PKEY_CTX_set1_hkdf_salt(pctx.get(), salt_ptr, salt.size()) <= 0 || EVP_PKEY_CTX_set1_hkdf_key(pctx.get(), ikm_ptr, ikm.size()) <= 0 || EVP_PKEY_CTX_add1_hkdf_info(pctx.get(), info_ptr, info.size()) <= 0) { return util::Status(absl::StatusCode::kInternal, "EVP_PKEY_CTX setup failed"); } size_t output_length = out_key.size(); if (EVP_PKEY_derive(pctx.get(), out_key.data(), &output_length) <= 0) { return util::Status(absl::StatusCode::kInternal, "HKDF failed"); } return util::OkStatus(); #endif } } // namespace util::StatusOr Hkdf::ComputeHkdf(HashType hash, const util::SecretData &ikm, absl::string_view salt, absl::string_view info, size_t out_len) { util::StatusOr evp_md = internal::EvpHashFromHashType(hash); if (!evp_md.ok()) { return evp_md.status(); } util::SecretData out_key(out_len); util::Status result = SslHkdf(*evp_md, util::SecretDataAsStringView(ikm), salt, info, absl::MakeSpan(out_key.data(), out_key.size())); if (!result.ok()) { return result; } return out_key; } util::StatusOr Hkdf::ComputeHkdf(HashType hash, absl::string_view ikm, absl::string_view salt, absl::string_view info, size_t out_len) { util::StatusOr evp_md = internal::EvpHashFromHashType(hash); if (!evp_md.ok()) { return evp_md.status(); } std::string out_key; ResizeStringUninitialized(&out_key, out_len); util::Status result = SslHkdf( *evp_md, ikm, salt, info, absl::MakeSpan(reinterpret_cast(&out_key[0]), out_key.size())); if (!result.ok()) { return result; } return out_key; } util::StatusOr Hkdf::ComputeEciesHkdfSymmetricKey( HashType hash, absl::string_view kem_bytes, const util::SecretData &shared_secret, absl::string_view salt, absl::string_view info, size_t out_len) { util::SecretData ikm(kem_bytes.size() + shared_secret.size()); absl::c_copy(kem_bytes, ikm.begin()); absl::c_copy(shared_secret, ikm.begin() + kem_bytes.size()); return Hkdf::ComputeHkdf(hash, ikm, salt, info, out_len); } } // namespace subtle } // namespace tink } // namespace crypto