blob: f3fd16cdf13deede44b7bc065a501d2e92d0c08f [file]
// Copyright 2025 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/internal/proto_parser_message.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#include "absl/base/nullability.h"
#include "absl/crc/crc32c.h"
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "tink/internal/call_with_core_dump_protection.h"
#include "tink/internal/proto_parser_fields.h"
#include "tink/internal/proto_parser_state.h"
#include "tink/internal/proto_parsing_helpers.h"
#include "tink/internal/secret_buffer.h"
#include "tink/secret_data.h"
#include "tink/subtle/subtle_util.h"
#include "tink/util/secret_data.h"
namespace crypto {
namespace tink {
namespace internal {
namespace proto_parsing {
// Message.
bool Message::FieldsAreSorted() const {
for (size_t i = 1; i < num_fields(); ++i) {
if (field(i - 1)->FieldNumber() >= field(i)->FieldNumber()) {
return false;
}
}
return true;
}
const Field* /*absl_nullable - not yet supported*/ Message::FieldWithNumber(uint32_t field_number) {
// Assumes fields returned by field() are sorted in strictly increasing order
// of their field number.
ABSL_DCHECK(FieldsAreSorted())
<< "Fields returned by field() must be sorted in strictly increasing "
"order of their field number.";
int low = 0;
int high = num_fields() - 1;
while (low <= high) {
// Invariant: If field_number is in the array, it is in the range [low,
// high].
int mid = low + (high - low) / 2;
const Field* f = field(mid);
if (f->FieldNumber() == field_number) {
return f;
}
if (f->FieldNumber() < field_number) {
low = mid + 1;
} else {
high = mid - 1;
}
}
// field_number is not in the array.
return nullptr;
}
void Message::Clear() {
for (size_t i = 0; i < num_fields(); ++i) {
const_cast<Field*>(field(i))->Clear();
}
}
bool Message::ParseFromStringImpl(absl::string_view in,
absl::crc32c_t* /*absl_nullable - not yet supported*/ result_crc) {
Clear();
ParsingState state(in, result_crc);
if (!Parse(state)) {
return false;
}
ABSL_QCHECK(state.ParsingDone());
ABSL_QCHECK(state.RemainingData().empty());
return true;
}
bool Message::ParseFromString(absl::string_view in) {
return ParseFromStringImpl(in, /*result_crc=*/nullptr);
}
absl::StatusOr<util::SecretValue<absl::crc32c_t>>
Message::ParseFromStringWithCrc(absl::string_view in) {
auto result_crc = util::SecretValue<absl::crc32c_t>(absl::crc32c_t(0));
if (!ParseFromStringImpl(in, &result_crc.value())) {
return absl::InvalidArgumentError("Failed to parse message");
}
return result_crc;
}
SecretData Message::SerializeAsSecretData() const {
SecretBuffer out(ByteSizeLong());
auto buffer = absl::MakeSpan(reinterpret_cast<char*>(out.data()), out.size());
return CallWithCoreDumpProtection([&]() -> SecretData {
absl::crc32c_t result_crc = absl::crc32c_t(0);
auto serialization_state = SerializationState(buffer, &result_crc);
ABSL_QCHECK(Serialize(serialization_state));
ABSL_QCHECK(serialization_state.GetBuffer().empty());
#ifdef TINK_CPP_SECRET_DATA_IS_STD_VECTOR
return util::SecretDataFromStringView(out.AsStringView());
#else
return SecretData(std::move(out), result_crc);
#endif
});
}
std::string Message::SerializeAsString() const {
std::string out;
subtle::ResizeStringUninitialized(&out, ByteSizeLong());
SerializationState serialization_state(
absl::MakeSpan(reinterpret_cast<char*>(out.data()), out.size()));
ABSL_QCHECK(Serialize(serialization_state));
ABSL_QCHECK(serialization_state.GetBuffer().empty());
return out;
}
bool Message::Serialize(SerializationState& out) const {
for (size_t i = 0; i < num_fields(); ++i) {
if (!field(i)->SerializeWithTagInto(out)) {
return false;
}
}
return true;
}
size_t Message::ByteSizeLong() const {
size_t size = 0;
for (size_t i = 0; i < num_fields(); ++i) {
size += field(i)->GetSerializedSizeIncludingTag();
}
return size;
}
bool Message::Parse(ParsingState& in) {
while (!in.ParsingDone()) {
WireType wire_type;
int field_number;
if (!ConsumeIntoWireTypeAndFieldNumber(in, wire_type, field_number)) {
return false;
}
const Field* /*absl_nullable - not yet supported*/ field = FieldWithNumber(field_number);
if (field == nullptr || field->GetWireType() != wire_type) {
if (wire_type == WireType::kStartGroup) {
if (!SkipGroup(field_number, in)) {
return false;
}
} else {
if (!SkipField(wire_type, in)) {
return false;
}
}
continue;
}
BeforeFieldSet(field);
if (!const_cast<Field*>(field)->ConsumeIntoMember(in)) {
return false;
}
}
return true;
}
// MessageFieldBase.
bool MessageFieldBase::ConsumeIntoMember(ParsingState& serialized) {
uint32_t length;
if (!ConsumeVarintForSize(serialized, length)) {
return false;
}
if (length > serialized.RemainingData().size()) {
return false;
}
ParsingState submessage_parsing_state =
serialized.SplitOffSubmessageState(length);
Message* /*absl_nullable - not yet supported*/ msg = mutable_message();
ABSL_DCHECK(msg != nullptr);
if (!msg->Parse(submessage_parsing_state)) {
return false;
}
if (!submessage_parsing_state.ParsingDone()) {
ABSL_LOG(DFATAL) << "Submessage wasn't parsed correctly";
return false;
}
return true;
}
bool MessageFieldBase::SerializeWithTagInto(
SerializationState& out) const {
const Message* /*absl_nullable - not yet supported*/ msg = message();
if (msg == nullptr) {
return true;
}
if (!SerializeWireTypeAndFieldNumber(GetWireType(), FieldNumber(), out)) {
return false;
}
const size_t size = msg->ByteSizeLong();
if (!SerializeVarint(size, out)) {
return false;
}
if (out.GetBuffer().size() < size) {
return false;
}
// Serialize the msg.
return msg->Serialize(out);
}
size_t MessageFieldBase::GetSerializedSizeIncludingTag() const {
const Message* /*absl_nullable - not yet supported*/ msg = message();
if (msg == nullptr) {
return 0;
}
const size_t size = msg->ByteSizeLong();
return WireTypeAndFieldNumberLength(GetWireType(), FieldNumber()) +
VarintLength(size) + size;
}
// RepeatedMessageFieldBase.
bool RepeatedMessageFieldBase::ConsumeIntoMember(ParsingState& serialized) {
uint32_t length;
if (!ConsumeVarintForSize(serialized, length)) {
return false;
}
if (length > serialized.RemainingData().size()) {
return false;
}
ParsingState submessage_parsing_state =
serialized.SplitOffSubmessageState(length);
Message* to_add = add_message();
if (!to_add->Parse(submessage_parsing_state)) {
return false;
}
if (!submessage_parsing_state.ParsingDone()) {
ABSL_LOG(DFATAL) << "Submessage wasn't parsed correctly";
return false;
}
return true;
}
bool RepeatedMessageFieldBase::SerializeWithTagInto(
SerializationState& out) const {
for (size_t i = 0; i < num_messages(); ++i) {
const Message& msg = message(i);
if (!SerializeWireTypeAndFieldNumber(GetWireType(), FieldNumber(), out)) {
return false;
}
const size_t size = msg.ByteSizeLong();
if (!SerializeVarint(size, out)) {
return false;
}
if (out.GetBuffer().size() < size) {
return false;
}
// Serialize the message.
if (!msg.Serialize(out)) {
return false;
}
}
return true;
}
size_t RepeatedMessageFieldBase::GetSerializedSizeIncludingTag() const {
const size_t wire_type_and_field_number_length =
WireTypeAndFieldNumberLength(GetWireType(), FieldNumber());
size_t size = 0;
for (size_t i = 0; i < num_messages(); ++i) {
const Message& msg = message(i);
size += msg.ByteSizeLong() + VarintLength(msg.ByteSizeLong()) +
wire_type_and_field_number_length;
}
return size;
}
} // namespace proto_parsing
} // namespace internal
} // namespace tink
} // namespace crypto