| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "device/fido/prf_input.h" |
| |
| #include <array> |
| |
| #include "base/containers/to_array.h" |
| #include "components/cbor/values.h" |
| #include "device/fido/public/fido_constants.h" |
| #include "third_party/boringssl/src/include/openssl/digest.h" |
| #include "third_party/boringssl/src/include/openssl/hmac.h" |
| #include "third_party/boringssl/src/include/openssl/sha.h" |
| |
| namespace device { |
| |
| namespace { |
| bool CBORToPRFValue(const cbor::Value& v, std::array<uint8_t, 32>* out) { |
| CHECK(out); |
| if (!v.is_bytestring() || v.GetBytestring().size() != out->size()) { |
| return false; |
| } |
| std::ranges::copy(v.GetBytestring(), out->begin()); |
| return true; |
| } |
| |
| // HashPRFValue hashes a PRF evaluation point with a fixed prefix in order to |
| // separate the set of points that a website can evaluate. See |
| // https://w3c.github.io/webauthn/#prf-extension. |
| std::array<uint8_t, 32> HashPRFValue(base::span<const uint8_t> value) { |
| constexpr char kPrefix[] = "WebAuthn PRF"; |
| |
| SHA256_CTX ctx; |
| SHA256_Init(&ctx); |
| // This deliberately includes the terminating NUL. |
| SHA256_Update(&ctx, kPrefix, sizeof(kPrefix)); |
| SHA256_Update(&ctx, value.data(), value.size()); |
| |
| std::array<uint8_t, 32> digest; |
| SHA256_Final(digest.data(), &ctx); |
| return digest; |
| } |
| } // namespace |
| |
| PRFInput::PRFInput() = default; |
| PRFInput::PRFInput(const PRFInput&) = default; |
| PRFInput::PRFInput(PRFInput&&) = default; |
| PRFInput& PRFInput::operator=(const PRFInput&) = default; |
| PRFInput::~PRFInput() = default; |
| |
| // static |
| std::optional<PRFInput> PRFInput::FromCBOR(const cbor::Value& v) { |
| if (!v.is_map()) { |
| return std::nullopt; |
| } |
| const cbor::Value::MapValue& map = v.GetMap(); |
| const auto first_it = map.find(cbor::Value(kExtensionPRFFirst)); |
| if (first_it == map.end()) { |
| return std::nullopt; |
| } |
| |
| PRFInput ret; |
| if (!CBORToPRFValue(first_it->second, &ret.salt1)) { |
| return std::nullopt; |
| } |
| |
| const auto second_it = map.find(cbor::Value(kExtensionPRFSecond)); |
| if (second_it != map.end()) { |
| ret.salt2.emplace(); |
| if (!CBORToPRFValue(second_it->second, &ret.salt2.value())) { |
| return std::nullopt; |
| } |
| } |
| return ret; |
| } |
| |
| cbor::Value::MapValue PRFInput::ToCBOR() const { |
| cbor::Value::MapValue ret; |
| ret.emplace(kExtensionPRFFirst, |
| std::vector<uint8_t>(this->salt1.begin(), this->salt1.end())); |
| if (this->salt2) { |
| ret.emplace(kExtensionPRFSecond, |
| std::vector<uint8_t>(this->salt2->begin(), this->salt2->end())); |
| } |
| return ret; |
| } |
| |
| void PRFInput::HashInputsIntoSalts() { |
| this->salt1 = HashPRFValue(this->input1); |
| if (this->input2) { |
| this->salt2 = HashPRFValue(*(this->input2)); |
| } |
| } |
| |
| std::vector<uint8_t> PRFInput::EvaluateHMAC( |
| base::span<const uint8_t> hmac_key, |
| const std::array<uint8_t, 32>& hmac_salt1, |
| const std::optional<std::array<uint8_t, 32>>& hmac_salt2) { |
| uint8_t hmac_result[SHA256_DIGEST_LENGTH]; |
| unsigned hmac_out_length; |
| HMAC(EVP_sha256(), hmac_key.data(), hmac_key.size(), hmac_salt1.data(), |
| hmac_salt1.size(), hmac_result, &hmac_out_length); |
| CHECK_EQ(hmac_out_length, sizeof(hmac_result)); |
| |
| std::vector<uint8_t> outputs; |
| outputs.insert(outputs.end(), std::begin(hmac_result), std::end(hmac_result)); |
| |
| if (hmac_salt2) { |
| HMAC(EVP_sha256(), hmac_key.data(), hmac_key.size(), hmac_salt2->data(), |
| hmac_salt2->size(), hmac_result, &hmac_out_length); |
| CHECK_EQ(hmac_out_length, sizeof(hmac_result)); |
| outputs.insert(outputs.end(), std::begin(hmac_result), |
| std::end(hmac_result)); |
| } |
| return outputs; |
| } |
| |
| std::vector<uint8_t> PRFInput::EvaluateHMAC( |
| base::span<const uint8_t> hmac_key) const { |
| return EvaluateHMAC(hmac_key, this->salt1, this->salt2); |
| } |
| |
| } // namespace device |