blob: 5feaa3b10411d0e2487c0bb3e67f8d7490ff76f8 [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.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef TINK_INTERNAL_PROTO_PARSER_MESSAGE_H_
#define TINK_INTERNAL_PROTO_PARSER_MESSAGE_H_
#include <cstddef>
#include <cstdint>
#include <string>
#include <type_traits>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/base/no_destructor.h"
#include "absl/base/nullability.h"
#include "absl/crc/crc32c.h"
#include "absl/log/absl_check.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "tink/internal/proto_parser_fields.h"
#include "tink/internal/proto_parser_options.h"
#include "tink/internal/proto_parser_state.h"
#include "tink/internal/proto_parsing_helpers.h"
#include "tink/secret_data.h"
#include "tink/util/secret_data.h"
ABSL_POINTERS_DEFAULT_NONNULL
namespace crypto {
namespace tink {
namespace internal {
namespace proto_parsing {
// Represents a proto message.
//
// Usage:
//
// class MyMessage : public Message {
// public:
// MyMessage() : Message(&fields_) {}
// ~MyMessage() = default;
//
// private:
// size_t num_fields() const override { return 2; }
// const Field* field(int i) const override {
// return std::array<const Field*, 2>{&some_string_,
// &some_other_string_}[i];
// }
// BytesField some_string_{1};
// SecretDataField some_other_string_{2};
// };
//
// This class is not thread-safe.
class Message {
public:
Message() = default;
virtual ~Message() = default;
// Copyable and movable.
Message(const Message&) = default;
Message& operator=(const Message&) = default;
Message(Message&&) noexcept = default;
Message& operator=(Message&&) noexcept = default;
// Methods taken from the proto2::Message interface.
// Clears all fields.
virtual void Clear();
bool ParseFromString(absl::string_view in);
size_t ByteSizeLong() const;
// Parses `in` and returns the CRC of the input.
//
// For fields that support CRCs (e.g. SecretData), the CRC of the field is
// consistent with the returned CRC. This enables end-to-end coverage of
// the CRC computation: if the returned CRC is known to be correct, the CRCs
// of the individual fields must also be correct.
absl::StatusOr<util::SecretValue<absl::crc32c_t>> ParseFromStringWithCrc(
absl::string_view in);
// Serializes the message as SecretData.
SecretData SerializeAsSecretData() const;
protected:
std::string SerializeAsString() const;
private:
friend class MessageFieldBase;
friend class RepeatedMessageFieldBase;
bool FieldsAreSorted() const;
// Returns the field with the given `field_number`, or nullptr if no such
// field exists.
const Field* /*absl_nullable - not yet supported*/ FieldWithNumber(uint32_t field_number);
bool ParseFromStringImpl(absl::string_view in,
absl::crc32c_t* /*absl_nullable - not yet supported*/ result_crc);
// Serializes the message into the given serialization state `out`.
// Returns true if the serialization was successful.
bool Serialize(SerializationState& out) const;
// Parses the message from the given parsing state `in`. Returns true if the
// parsing was successful.
bool Parse(ParsingState& in);
protected:
// Returns the number of fields in the message.
virtual size_t num_fields() const = 0;
// Returns the `i`-th field.
//
// Preconditions:
// * 0 <= i < num_fields().
// * The fields are sorted by field number.
virtual const Field* field(int i) const = 0;
// Hook called during parsing or setter invocation when a field belongs to a
// oneof.
virtual void BeforeFieldSet(const Field* field) {}
};
// Base class for RepeatedMessageField.
//
// It implements all methods of `Field` but `Clear`.
class RepeatedMessageFieldBase : public Field {
public:
explicit RepeatedMessageFieldBase(int field_number)
: Field(field_number, WireType::kLengthDelimited) {}
// Copyable and movable.
RepeatedMessageFieldBase(const RepeatedMessageFieldBase&) = default;
RepeatedMessageFieldBase& operator=(const RepeatedMessageFieldBase&) =
default;
RepeatedMessageFieldBase(RepeatedMessageFieldBase&&) noexcept = default;
RepeatedMessageFieldBase& operator=(RepeatedMessageFieldBase&&) noexcept =
default;
bool ConsumeIntoMember(ParsingState& serialized) override;
bool SerializeWithTagInto(SerializationState& out) const override;
size_t GetSerializedSizeIncludingTag() const override;
protected:
// Returns the i-th message.
virtual const Message& message(int i) const = 0;
// Adds a new message and returns a pointer to it.
virtual Message* add_message() = 0;
// Returns the number of messages.
virtual size_t num_messages() const = 0;
};
// Represents a repeated proto message.
//
// Usage:
//
// class MyMessage : public Message {
// public:
// MyMessage() : Message(&fields_) {}
// ~MyMessage() = default;
// private:
// RepeatedMessageField<MySubMessage> some_repeated_message_{1};
// Fields fields_{&some_repeated_message_};
// };
//
// This class is not thread-safe.
template <typename MessageT>
class RepeatedMessageField : public RepeatedMessageFieldBase {
public:
explicit RepeatedMessageField(int field_number)
: RepeatedMessageFieldBase(field_number) {}
// Copyable and movable.
RepeatedMessageField(const RepeatedMessageField&) = default;
RepeatedMessageField& operator=(const RepeatedMessageField&) = default;
RepeatedMessageField(RepeatedMessageField&&) noexcept = default;
RepeatedMessageField& operator=(RepeatedMessageField&&) noexcept = default;
void Clear() override { values_.clear(); }
// See https://protobuf.dev/reference/cpp/cpp-generated/#repeatedmessage.
int values_size() const { return values_.size(); }
const MessageT& values(int index) const { return values_[index]; }
MessageT* mutable_values(int index) { return &values_[index]; }
MessageT* add_values() {
values_.emplace_back();
return &values_.back();
}
const std::vector<MessageT>& values() const { return values_; }
std::vector<MessageT>* mutable_values() { return &values_; }
private:
const Message& message(int i) const override { return values(i); }
Message* add_message() override { return add_values(); }
size_t num_messages() const override { return values_size(); }
std::vector<MessageT> values_;
};
// Base class for MessageField.
//
// It implements all methods of `Field` but `Clear`.
class MessageFieldBase : public Field {
public:
explicit MessageFieldBase(int field_number)
: Field(field_number, WireType::kLengthDelimited) {}
// Copyable and movable.
MessageFieldBase(const MessageFieldBase&) = default;
MessageFieldBase& operator=(const MessageFieldBase&) = default;
MessageFieldBase(MessageFieldBase&&) noexcept = default;
MessageFieldBase& operator=(MessageFieldBase&&) noexcept = default;
bool ConsumeIntoMember(ParsingState& serialized) override;
bool SerializeWithTagInto(SerializationState& out) const override;
size_t GetSerializedSizeIncludingTag() const override;
protected:
// Returns the message if it is set, otherwise nullptr.
virtual const Message* /*absl_nullable - not yet supported*/ message() const = 0;
// Returns a mutable message, which is guaranteed to be non-null.
virtual Message* mutable_message() = 0;
};
// Represents a field of type proto message.
//
// Usage:
//
// class MyMessage : public Message {
// public:
// MyMessage() : Message(&fields_) {}
// ~MyMessage() = default;
//
// private:
// size_t num_fields() const override { return 2; }
// const Field* field(int i) const override {
// return std::array<const Field*, 2>{&some_string_,
// &some_other_string_}[i];
// }
//
// MessageField<MySubMessage> some_message_{1};
// SecretDataField some_other_string_{2};
// };
//
// Note:
// * if options == ProtoFieldOptions::kAlwaysPresent, then the field is
// always present (i.e., has_value() never returns false). This forces
// serialization as well, which is useful if the field is LEGACY_REQUIRED in
// proto.
// * if options == ProtoFieldOptions::kExplicit, then the field is serialized
// only if the value is set (even if with a default value).
//
// This class is not thread-safe.
template <typename MessageT>
class MessageField : public MessageFieldBase {
static_assert(std::is_copy_constructible_v<MessageT>,
"MessageT must be copy constructible.");
static_assert(std::is_copy_assignable_v<MessageT>,
"MessageT must be copy assignable.");
static_assert(std::is_move_constructible_v<MessageT>,
"MessageT must be move constructible.");
static_assert(std::is_move_assignable_v<MessageT>,
"MessageT must be move assignable.");
public:
explicit MessageField(int field_number, ProtoFieldOptions options =
ProtoFieldOptions::kExplicit)
: MessageFieldBase(field_number), options_(options) {
ABSL_CHECK(options_ != ProtoFieldOptions::kImplicit)
<< "MessageField does not support kImplicit option.";
Clear();
}
// Copyable and movable.
MessageField(const MessageField&) = default;
MessageField& operator=(const MessageField&) = default;
MessageField(MessageField&&) noexcept = default;
MessageField& operator=(MessageField&&) noexcept = default;
// Clears the field.
//
// If options_ == ProtoFieldOptions::kAlwaysPresent then the field is set to
// the default value. Otherwise the optional field is cleared.
void Clear() override {
if (options_ == ProtoFieldOptions::kAlwaysPresent) {
value_.emplace();
} else {
value_.reset();
}
}
// APIs. See
// https://protobuf.dev/reference/cpp/cpp-generated/#embeddedmessage.
// Returns whether the field has value.
//
// If options_ == ProtoFieldOptions::kAlwaysPresent this is always true as the
// class guarantees that value_ always has a value.
bool has_value() const { return value_.has_value(); }
const MessageT& value() const {
if (value_.has_value()) {
return *value_;
}
return DefaultValue();
}
MessageT* mutable_value() {
if (!value_.has_value()) {
value_.emplace();
}
return &value_.value();
}
private:
const Message* /*absl_nullable - not yet supported*/ message() const override {
return value_.has_value() ? &*value_ : nullptr;
}
Message* mutable_message() override {
if (!value_.has_value()) {
value_.emplace();
}
return &value_.value();
}
ABSL_ATTRIBUTE_NOINLINE
const MessageT& DefaultValue() const {
static const absl::NoDestructor<MessageT> default_value;
return *default_value;
}
absl::optional<MessageT> value_ = std::nullopt;
ProtoFieldOptions options_;
};
} // namespace proto_parsing
} // namespace internal
} // namespace tink
} // namespace crypto
#endif // TINK_INTERNAL_PROTO_PARSER_MESSAGE_H_