blob: 9efc6278d4bf88a246d642ef2341a9b1f266625e [file]
// 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/slh_dsa_private_key.h"
#include <cstdint>
#include <string>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.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
#include "tink/internal/secret_buffer.h"
#include "tink/secret_data.h"
#include "tink/signature/slh_dsa_parameters.h"
#include "tink/util/secret_data.h"
#if defined(OPENSSL_IS_BORINGSSL) && !defined(TINK_USE_ONLY_FIPS)
#include "openssl/mem.h"
#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/key.h"
#include "tink/partial_key_access_token.h"
#include "tink/restricted_data.h"
#include "tink/signature/slh_dsa_public_key.h"
#if defined(OPENSSL_IS_BORINGSSL) && !defined(TINK_USE_ONLY_FIPS)
#include "tink/signature/internal/slh_dsa_parameter_set.h"
#endif
namespace crypto {
namespace tink {
namespace {
#if defined(OPENSSL_IS_BORINGSSL) && !defined(TINK_USE_ONLY_FIPS)
absl::Status GetPublicFromPrivate(
const internal::SlhDsaParameterSet& parameter_set,
std::string& public_key_bytes, const SecretData& private_key_bytes) {
if (parameter_set == internal::SlhDsaParameterSet::Sha2_128s()) {
SLHDSA_SHA2_128S_public_from_private(
reinterpret_cast<uint8_t*>(&public_key_bytes[0]),
private_key_bytes.data());
return absl::OkStatus();
}
if (parameter_set == internal::SlhDsaParameterSet::Shake_256f()) {
SLHDSA_SHAKE_256F_public_from_private(
reinterpret_cast<uint8_t*>(&public_key_bytes[0]),
private_key_bytes.data());
return absl::OkStatus();
}
return absl::Status(
absl::StatusCode::kInvalidArgument,
"SLH-DSA parameter combination is not supported by BoringSSL.");
}
absl::Status GetKeyPairFromSeed(
const internal::SlhDsaParameterSet& parameter_set,
std::string& public_key_bytes, internal::SecretBuffer& private_key_bytes,
const SecretData& private_seed_bytes) {
if (parameter_set == internal::SlhDsaParameterSet::Sha2_128s()) {
SLHDSA_SHA2_128S_generate_key_from_seed(
reinterpret_cast<uint8_t*>(&public_key_bytes[0]),
private_key_bytes.data(), private_seed_bytes.data());
return absl::OkStatus();
}
if (parameter_set == internal::SlhDsaParameterSet::Shake_256f()) {
SLHDSA_SHAKE_256F_generate_key_from_seed(
reinterpret_cast<uint8_t*>(&public_key_bytes[0]),
private_key_bytes.data(), private_seed_bytes.data());
return absl::OkStatus();
}
return absl::Status(
absl::StatusCode::kInvalidArgument,
"SLH-DSA parameter combination is not supported by BoringSSL.");
}
#endif
} // namespace
absl::StatusOr<SlhDsaPrivateKey> SlhDsaPrivateKey::Create(
const SlhDsaPublicKey& public_key, const RestrictedData& private_key_bytes,
PartialKeyAccessToken token) {
#if !defined(OPENSSL_IS_BORINGSSL) || defined(TINK_USE_ONLY_FIPS)
return absl::UnimplementedError(
"SLH-DSA is only supported in non-FIPS BoringSSL builds.");
#else
// Only 64-byte, 96-byte and 128-byte private keys are supported.
if (private_key_bytes.size() != 64 && private_key_bytes.size() != 96 &&
private_key_bytes.size() != 128) {
return absl::Status(
absl::StatusCode::kInvalidArgument,
"SLH-DSA private key length must be 64, 96, or 128 bytes.");
}
if (public_key.GetParameters().GetPrivateKeySizeInBytes() !=
private_key_bytes.size()) {
return absl::Status(absl::StatusCode::kInvalidArgument,
"Private key size does not match parameters");
}
absl::StatusOr<internal::SlhDsaParameterSet> parameter_set =
internal::GetSlhDsaParameterSet(public_key.GetParameters());
if (!parameter_set.ok()) {
return parameter_set.status();
}
int public_key_size = parameter_set->GetPublicKeySizeInBytes();
// Confirm that the private key and public key are a valid SLH-DSA key pair.
std::string public_key_bytes_regen;
public_key_bytes_regen.resize(public_key_size);
absl::Status status = absl::OkStatus();
internal::CallWithCoreDumpProtection([&]() {
internal::ScopedAssumeRegionCoreDumpSafe scope(&public_key_bytes_regen[0],
public_key_size);
status = GetPublicFromPrivate(
*parameter_set, public_key_bytes_regen,
private_key_bytes.Get(InsecureSecretKeyAccess::Get()));
internal::DfsanClearLabel(&public_key_bytes_regen[0], public_key_size);
});
if (!status.ok()) {
return status;
}
absl::string_view expected_public_key_bytes =
public_key.GetPublicKeyBytes(token);
if (CRYPTO_memcmp(expected_public_key_bytes.data(),
public_key_bytes_regen.data(), public_key_size) != 0) {
return absl::Status(absl::StatusCode::kInvalidArgument,
"Invalid SLH-DSA key pair");
}
return SlhDsaPrivateKey(public_key, private_key_bytes);
#endif
}
absl::StatusOr<SlhDsaPrivateKey> SlhDsaPrivateKey::Create(
const SlhDsaParameters& parameters, const RestrictedData& private_key_bytes,
absl::optional<int> id_requirement, PartialKeyAccessToken token) {
#if !defined(OPENSSL_IS_BORINGSSL) || defined(TINK_USE_ONLY_FIPS)
return absl::UnimplementedError(
"SLH-DSA is only supported in non-FIPS BoringSSL builds.");
#else
// Only 64-byte, 96-byte and 128-byte private keys are supported.
if (private_key_bytes.size() != 64 && private_key_bytes.size() != 96 &&
private_key_bytes.size() != 128) {
return absl::Status(
absl::StatusCode::kInvalidArgument,
"SLH-DSA private key length must be 64, 96, or 128 bytes.");
}
if (parameters.GetPrivateKeySizeInBytes() != private_key_bytes.size()) {
return absl::Status(absl::StatusCode::kInvalidArgument,
"Private key size does not match parameters");
}
absl::StatusOr<internal::SlhDsaParameterSet> parameter_set =
internal::GetSlhDsaParameterSet(parameters);
if (!parameter_set.ok()) {
return parameter_set.status();
}
int public_key_size = parameter_set->GetPublicKeySizeInBytes();
std::string public_key_bytes;
public_key_bytes.resize(public_key_size);
absl::Status status = absl::OkStatus();
internal::CallWithCoreDumpProtection([&]() {
internal::ScopedAssumeRegionCoreDumpSafe scope(&public_key_bytes[0],
public_key_size);
absl::Status status = GetPublicFromPrivate(
*parameter_set, public_key_bytes,
private_key_bytes.Get(InsecureSecretKeyAccess::Get()));
internal::DfsanClearLabel(&public_key_bytes[0], public_key_size);
});
if (!status.ok()) {
return status;
}
absl::StatusOr<SlhDsaPublicKey> public_key = SlhDsaPublicKey::Create(
parameters, public_key_bytes, id_requirement, token);
if (!public_key.ok()) {
return public_key.status();
}
return SlhDsaPrivateKey(*public_key, private_key_bytes);
#endif
}
absl::StatusOr<SlhDsaPrivateKey> SlhDsaPrivateKey::CreateFromSeed(
const SlhDsaParameters& parameters,
const RestrictedData& private_seed_bytes,
absl::optional<int> id_requirement, PartialKeyAccessToken token) {
#if !defined(OPENSSL_IS_BORINGSSL) || defined(TINK_USE_ONLY_FIPS)
return absl::UnimplementedError(
"SLH-DSA is only supported in non-FIPS BoringSSL builds.");
#else
// Only 48-byte, 72-byte and 96-byte private keys are supported.
if (private_seed_bytes.size() != 48 && private_seed_bytes.size() != 72 &&
private_seed_bytes.size() != 96) {
return absl::Status(
absl::StatusCode::kInvalidArgument,
"SLH-DSA private seed length must be 48, 72, or 96 bytes.");
}
absl::StatusOr<internal::SlhDsaParameterSet> parameter_set =
internal::GetSlhDsaParameterSet(parameters);
if (!parameter_set.ok()) {
return parameter_set.status();
}
if (parameter_set->GetPrivateSeedSizeInBytes() != private_seed_bytes.size()) {
return absl::Status(absl::StatusCode::kInvalidArgument,
"Private key size does not match parameters");
}
int public_key_size = parameter_set->GetPublicKeySizeInBytes();
int private_key_size = parameter_set->GetPrivateKeySizeInBytes();
std::string public_key_bytes;
public_key_bytes.resize(public_key_size);
internal::SecretBuffer private_key_bytes(private_key_size);
absl::Status status = absl::OkStatus();
internal::CallWithCoreDumpProtection([&]() {
internal::ScopedAssumeRegionCoreDumpSafe scope(&public_key_bytes[0],
public_key_size);
status = GetKeyPairFromSeed(
*parameter_set, public_key_bytes, private_key_bytes,
private_seed_bytes.Get(InsecureSecretKeyAccess::Get()));
internal::DfsanClearLabel(&public_key_bytes[0], public_key_size);
});
if (!status.ok()) {
return status;
}
absl::StatusOr<SlhDsaPublicKey> public_key = SlhDsaPublicKey::Create(
parameters, public_key_bytes, id_requirement, token);
if (!public_key.ok()) {
return public_key.status();
}
return SlhDsaPrivateKey(
*public_key,
RestrictedData(util::internal::AsSecretData(private_key_bytes),
InsecureSecretKeyAccess::Get()));
#endif
}
bool SlhDsaPrivateKey::operator==(const Key& other) const {
const SlhDsaPrivateKey* that = dynamic_cast<const SlhDsaPrivateKey*>(&other);
if (that == nullptr) {
return false;
}
return public_key_ == that->public_key_ &&
private_key_bytes_ == that->private_key_bytes_;
}
} // namespace tink
} // namespace crypto