blob: 896ad7e78fae04f4159616f7c20cc28c88f3f5e8 [file] [edit]
// Copyright 2026 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
//
// https://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 "claim/local_signer.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/log/absl_log.h"
#include "absl/status/status.h"
#include "absl/status/status_macros.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "async/async_context.h"
#include "claim/signer.h"
#include "cose/sig_structure.h"
#include "cose/timestamp_creator.h"
#include "crypto/crypto_write_handler.h"
#include "crypto/hash.h"
#include "utils/cbor_padding.h"
namespace credentio {
namespace {
absl::StatusOr<std::string> FinalizeSignature(
const absl::StatusOr<std::string>& timestamp,
TimeStampingMode time_stamping_mode, uint64_t max_timestamp_byte_size,
CoseSign1TaggedStructure* cose_sign1, uint64_t max_signature_byte_size,
std::optional<size_t> pad_to_size) {
if (!timestamp.ok() && time_stamping_mode == TimeStampingMode::kRequired) {
return absl::Status(timestamp.status().code(),
absl::StrCat("timestamping is required but failed: ",
timestamp.status().message()));
}
if (timestamp.ok()) {
if (timestamp.value().size() > max_timestamp_byte_size) {
if (time_stamping_mode == TimeStampingMode::kRequired) {
return absl::InternalError(
absl::StrCat("Timestamp size ", timestamp.value().size(),
" is larger than the maximum allowed size ",
max_timestamp_byte_size));
} else {
ABSL_LOG_EVERY_N_SEC(WARNING, 60)
<< "Ignoring timestamp because the timestamp size "
<< timestamp.value().size()
<< " is larger than the maximum allowed size "
<< max_timestamp_byte_size;
}
} else if (!timestamp.value().empty()) {
cose_sign1->unprotected_header.sig_tst2 =
TstContainer{.tst_tokens = {{.val = timestamp.value()}}};
}
}
if (pad_to_size.has_value()) {
// This logic is necessary for remote signing fallback to local where
// the template and final signatures must be padded to the same size.
// e.g. template uses remote signing but final uses local signing.
ABSL_RETURN_IF_ERROR(
PadCoseSign1TaggedStructure(*cose_sign1, *pad_to_size));
} else {
ABSL_RETURN_IF_ERROR(PadCoseSign1TaggedStructure(
*cose_sign1, max_signature_byte_size, max_timestamp_byte_size));
}
return EncodeCoseSign1TaggedStructure(*cose_sign1);
}
} // namespace
void LocalClaimSigner::Sign(
absl::string_view claim, Options options,
absl::AnyInvocable<void(absl::StatusOr<SignResult>) &&> cb) const {
AsyncResolver<SignResult> resolver(options.async_context, std::move(cb));
auto& signer = crypto_writer_.GetSigner();
auto protected_header_res = EncodeProtectedHeader(
ProtectedHeader{.alg = signer.algorithm(),
.certificate_chain = signer.certificate_chain()});
if (!protected_header_res.ok()) {
resolver.Return(protected_header_res.status());
return;
}
std::string protected_header = *std::move(protected_header_res);
Sig1Structure sig_structure{
.context = "Signature1",
.body_protected = std::move(protected_header),
.payload = std::string(claim),
};
auto sign_res = signer.Sign(EncodeSig1Structure(sig_structure));
if (!sign_res.ok()) {
resolver.Return(sign_res.status());
return;
}
if (sign_res->signature.size() > signer.max_signature_byte_size()) {
resolver.Return(absl::InternalError(
absl::StrCat("Signature size ", sign_res->signature.size(),
" is larger than the maximum allowed size ",
signer.max_signature_byte_size())));
return;
}
auto cose_sign1 = std::make_shared<CoseSign1TaggedStructure>();
cose_sign1->protected_header = sig_structure.body_protected;
cose_sign1->signature = std::move(sign_res->signature);
// Add OCSP responses.
const std::vector<std::string>& ocsp_responses = signer.ocsp_responses();
if (!ocsp_responses.empty()) {
cose_sign1->unprotected_header.ocsp_responses = ocsp_responses;
}
// Create timestamp and finalize signature.
uint64_t max_signature_byte_size = signer.max_signature_byte_size();
uint64_t max_timestamp_byte_size =
crypto_writer_.max_timestamp_byte_size(hasher_factory_.algorithm());
auto time_stamping_mode = options.time_stamping_mode;
if (time_stamping_mode == TimeStampingMode::kOmittedWithPadding) {
auto signature = FinalizeSignature(
/*timestamp=*/"", time_stamping_mode, max_timestamp_byte_size,
cose_sign1.get(), max_signature_byte_size, pad_to_size_);
if (!signature.ok()) {
resolver.Return(signature.status());
return;
}
SignResult sign_result{.signature = std::move(*signature)};
resolver.Return(std::move(sign_result));
return;
}
TimestampCreator::Options timestamp_creator_options{
.async_context = options.async_context,
};
auto create_timestamp_cb =
[resolver = std::move(resolver), max_timestamp_byte_size, cose_sign1,
max_signature_byte_size, pad_to_size = pad_to_size_, time_stamping_mode](
absl::StatusOr<std::string> timestamp_result) mutable {
auto signature = FinalizeSignature(
timestamp_result, time_stamping_mode, max_timestamp_byte_size,
cose_sign1.get(), max_signature_byte_size, pad_to_size);
if (!signature.ok()) {
resolver.Return(signature.status());
return;
}
SignResult sign_result{.signature = std::move(*signature)};
resolver.Return(std::move(sign_result));
};
timestamp_creator_.CreateTimestamp(*cose_sign1,
std::move(timestamp_creator_options),
std::move(create_timestamp_cb));
}
} // namespace credentio