| // Copyright 2024 Google LLC |
| // |
| // 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/signature/internal/slh_dsa_sign_boringssl.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/string_view.h" |
| // Every header in BoringSSL includes base.h, which in turn defines |
| // OPENSSL_IS_BORINGSSL. So we include this common header upfront here to |
| // "force" the definition of OPENSSL_IS_BORINGSSL in case BoringSSL is used. |
| #include "openssl/crypto.h" |
| #include "tink/internal/fips_utils.h" // IWYU pragma: keep |
| #if defined(OPENSSL_IS_BORINGSSL) && !defined(TINK_USE_ONLY_FIPS) |
| #include "openssl/slhdsa.h" |
| #endif |
| #include "tink/insecure_secret_key_access.h" |
| #include "tink/internal/call_with_core_dump_protection.h" |
| #include "tink/internal/dfsan_forwarders.h" |
| #include "tink/partial_key_access.h" |
| #include "tink/public_key_sign.h" |
| #include "tink/signature/internal/slh_dsa_parameter_set.h" |
| #include "tink/signature/slh_dsa_private_key.h" |
| #include "tink/subtle/subtle_util.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace internal { |
| |
| namespace { |
| |
| #if defined(OPENSSL_IS_BORINGSSL) && !defined(TINK_USE_ONLY_FIPS) |
| using SignFunc = int (*)(uint8_t*, const uint8_t*, const uint8_t*, size_t, |
| const uint8_t*, size_t); |
| |
| absl::StatusOr<SignFunc> GetSignFunc(const SlhDsaParameterSet& parameter_set) { |
| if (parameter_set == SlhDsaParameterSet::Sha2_128s()) { |
| return SLHDSA_SHA2_128S_sign; |
| } |
| if (parameter_set == SlhDsaParameterSet::Shake_256f()) { |
| return SLHDSA_SHAKE_256F_sign; |
| } |
| return absl::Status( |
| absl::StatusCode::kInvalidArgument, |
| "SLH-DSA parameter combination is not supported by BoringSSL."); |
| } |
| |
| absl::StatusOr<size_t> GetSignatureSize( |
| const SlhDsaParameterSet& parameter_set) { |
| if (parameter_set == SlhDsaParameterSet::Sha2_128s()) { |
| return SLHDSA_SHA2_128S_SIGNATURE_BYTES; |
| } |
| if (parameter_set == SlhDsaParameterSet::Shake_256f()) { |
| return SLHDSA_SHAKE_256F_SIGNATURE_BYTES; |
| } |
| return absl::Status( |
| absl::StatusCode::kInvalidArgument, |
| "SLH-DSA parameter combination is not supported by BoringSSL."); |
| } |
| |
| class SlhDsaSignBoringSsl : public PublicKeySign { |
| public: |
| static constexpr crypto::tink::internal::FipsCompatibility kFipsStatus = |
| crypto::tink::internal::FipsCompatibility::kNotFips; |
| |
| explicit SlhDsaSignBoringSsl(const SlhDsaPrivateKey& private_key) |
| : private_key_(private_key) {} |
| |
| ~SlhDsaSignBoringSsl() override = default; |
| |
| // Computes the signature for 'data'. |
| absl::StatusOr<std::string> Sign(absl::string_view data) const override; |
| |
| private: |
| SlhDsaPrivateKey private_key_; |
| }; |
| |
| absl::StatusOr<std::string> SlhDsaSignBoringSsl::Sign( |
| absl::string_view data) const { |
| absl::StatusOr<SlhDsaParameterSet> parameter_set = |
| GetSlhDsaParameterSet(private_key_.GetPublicKey().GetParameters()); |
| if (!parameter_set.ok()) { |
| return parameter_set.status(); |
| } |
| |
| absl::StatusOr<SignFunc> sign_func = GetSignFunc(*parameter_set); |
| if (!sign_func.ok()) { |
| return sign_func.status(); |
| } |
| |
| absl::StatusOr<size_t> signature_size = GetSignatureSize(*parameter_set); |
| if (!signature_size.ok()) { |
| return signature_size.status(); |
| } |
| |
| // The signature will be prepended with the output prefix for TINK keys. |
| std::string signature(private_key_.GetOutputPrefix()); |
| size_t signature_buffer_size = |
| *signature_size + private_key_.GetOutputPrefix().size(); |
| subtle::ResizeStringUninitialized(&signature, signature_buffer_size); |
| |
| internal::CallWithCoreDumpProtection([&, sign_func_val = *sign_func]() { |
| internal::ScopedAssumeRegionCoreDumpSafe scope(&signature[0], |
| signature_buffer_size); |
| sign_func_val(reinterpret_cast<uint8_t*>( |
| &signature[0] + private_key_.GetOutputPrefix().size()), |
| private_key_.GetPrivateKeyBytes(GetPartialKeyAccess()) |
| .Get(InsecureSecretKeyAccess::Get()) |
| .data(), |
| reinterpret_cast<const uint8_t*>(data.data()), data.size(), |
| /* context = */ nullptr, /* context_len = */ 0); |
| internal::DfsanClearLabel(&signature[0], signature_buffer_size); |
| }); |
| |
| return signature; |
| } |
| #endif |
| |
| } // namespace |
| |
| absl::StatusOr<std::unique_ptr<PublicKeySign>> NewSlhDsaSignBoringSsl( |
| const SlhDsaPrivateKey& private_key) { |
| #if !defined(OPENSSL_IS_BORINGSSL) || defined(TINK_USE_ONLY_FIPS) |
| return absl::UnimplementedError( |
| "SLH-DSA is only supported in non-FIPS BoringSSL builds."); |
| #else |
| auto status = internal::CheckFipsCompatibility<SlhDsaSignBoringSsl>(); |
| if (!status.ok()) { |
| return status; |
| } |
| absl::StatusOr<SlhDsaParameterSet> parameter_set = |
| GetSlhDsaParameterSet(private_key.GetPublicKey().GetParameters()); |
| if (!parameter_set.ok()) { |
| return parameter_set.status(); |
| } |
| |
| return {std::make_unique<SlhDsaSignBoringSsl>(std::move(private_key))}; |
| #endif |
| } |
| |
| } // namespace internal |
| } // namespace tink |
| } // namespace crypto |