xref: /aosp_15_r20/external/tink/cc/subtle/hkdf.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2017 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/hkdf.h"
18 
19 #include <cstdint>
20 #include <string>
21 
22 #include "absl/algorithm/container.h"
23 #include "absl/status/status.h"
24 #include "absl/strings/string_view.h"
25 #include "absl/types/span.h"
26 #include "openssl/evp.h"
27 // BoringSSL and OpenSSL have incompatible ways to compute HKDF: BoringSSL
28 // provides a one-shot API HKDF, while OpenSSL doesn't make that API public, but
29 // instead provides this functionality over the EVP interface, which in turn
30 // doesn't provide means to compute HKDF in BoringSSL. As a consequence, we need
31 // to selectively include the correct header and use different implementations.
32 #ifdef OPENSSL_IS_BORINGSSL
33 #include "openssl/base.h"
34 #include "openssl/hkdf.h"
35 #else
36 #include "openssl/kdf.h"
37 #endif
38 #include "tink/internal/md_util.h"
39 #include "tink/internal/ssl_unique_ptr.h"
40 #include "tink/subtle/common_enums.h"
41 #include "tink/subtle/subtle_util.h"
42 #include "tink/util/secret_data.h"
43 #include "tink/util/status.h"
44 #include "tink/util/statusor.h"
45 
46 namespace crypto {
47 namespace tink {
48 namespace subtle {
49 namespace {
50 
51 // Compute HKDF using `evp_md` hashing, key `ikm`, salt `salt` and info `info`.
52 // The result is written to `key`.
SslHkdf(const EVP_MD * evp_md,absl::string_view ikm,absl::string_view salt,absl::string_view info,absl::Span<uint8_t> out_key)53 util::Status SslHkdf(const EVP_MD *evp_md, absl::string_view ikm,
54                      absl::string_view salt, absl::string_view info,
55                      absl::Span<uint8_t> out_key) {
56   const uint8_t *ikm_ptr = reinterpret_cast<const uint8_t *>(ikm.data());
57   const uint8_t *salt_ptr = reinterpret_cast<const uint8_t *>(salt.data());
58   const uint8_t *info_ptr = reinterpret_cast<const uint8_t *>(info.data());
59 #ifdef OPENSSL_IS_BORINGSSL
60   if (HKDF(out_key.data(), out_key.size(), evp_md, ikm_ptr, ikm.size(),
61            salt_ptr, salt.size(), info_ptr, info.size()) != 1) {
62     return util::Status(absl::StatusCode::kInternal, "HKDF failed");
63   }
64   return util::OkStatus();
65 #else
66   internal::SslUniquePtr<EVP_PKEY_CTX> pctx(
67       EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, /*e=*/nullptr));
68   if (pctx == nullptr || EVP_PKEY_derive_init(pctx.get()) <= 0 ||
69       EVP_PKEY_CTX_set_hkdf_md(pctx.get(), evp_md) <= 0 ||
70       EVP_PKEY_CTX_set1_hkdf_salt(pctx.get(), salt_ptr, salt.size()) <= 0 ||
71       EVP_PKEY_CTX_set1_hkdf_key(pctx.get(), ikm_ptr, ikm.size()) <= 0 ||
72       EVP_PKEY_CTX_add1_hkdf_info(pctx.get(), info_ptr, info.size()) <= 0) {
73     return util::Status(absl::StatusCode::kInternal,
74                         "EVP_PKEY_CTX setup failed");
75   }
76   size_t output_length = out_key.size();
77   if (EVP_PKEY_derive(pctx.get(), out_key.data(), &output_length) <= 0) {
78     return util::Status(absl::StatusCode::kInternal, "HKDF failed");
79   }
80   return util::OkStatus();
81 #endif
82 }
83 
84 }  // namespace
85 
ComputeHkdf(HashType hash,const util::SecretData & ikm,absl::string_view salt,absl::string_view info,size_t out_len)86 util::StatusOr<util::SecretData> Hkdf::ComputeHkdf(HashType hash,
87                                                    const util::SecretData &ikm,
88                                                    absl::string_view salt,
89                                                    absl::string_view info,
90                                                    size_t out_len) {
91   util::StatusOr<const EVP_MD *> evp_md = internal::EvpHashFromHashType(hash);
92   if (!evp_md.ok()) {
93     return evp_md.status();
94   }
95 
96   util::SecretData out_key(out_len);
97   util::Status result =
98       SslHkdf(*evp_md, util::SecretDataAsStringView(ikm), salt, info,
99               absl::MakeSpan(out_key.data(), out_key.size()));
100   if (!result.ok()) {
101     return result;
102   }
103   return out_key;
104 }
105 
ComputeHkdf(HashType hash,absl::string_view ikm,absl::string_view salt,absl::string_view info,size_t out_len)106 util::StatusOr<std::string> Hkdf::ComputeHkdf(HashType hash,
107                                               absl::string_view ikm,
108                                               absl::string_view salt,
109                                               absl::string_view info,
110                                               size_t out_len) {
111   util::StatusOr<const EVP_MD *> evp_md = internal::EvpHashFromHashType(hash);
112   if (!evp_md.ok()) {
113     return evp_md.status();
114   }
115   std::string out_key;
116   ResizeStringUninitialized(&out_key, out_len);
117   util::Status result = SslHkdf(
118       *evp_md, ikm, salt, info,
119       absl::MakeSpan(reinterpret_cast<uint8_t *>(&out_key[0]), out_key.size()));
120   if (!result.ok()) {
121     return result;
122   }
123   return out_key;
124 }
125 
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)126 util::StatusOr<util::SecretData> Hkdf::ComputeEciesHkdfSymmetricKey(
127     HashType hash, absl::string_view kem_bytes,
128     const util::SecretData &shared_secret, absl::string_view salt,
129     absl::string_view info, size_t out_len) {
130   util::SecretData ikm(kem_bytes.size() + shared_secret.size());
131   absl::c_copy(kem_bytes, ikm.begin());
132   absl::c_copy(shared_secret, ikm.begin() + kem_bytes.size());
133   return Hkdf::ComputeHkdf(hash, ikm, salt, info, out_len);
134 }
135 
136 }  // namespace subtle
137 }  // namespace tink
138 }  // namespace crypto
139