This document describes the implementation of Early Data support (0-RTT) for DTLS v1.3 according to RFC 9147 Section 4.2.10. Early data allows clients to send application data immediately with the first flight of handshake messages, reducing connection latency from 1-RTT to 0-RTT for repeat connections.
Task 11: 0-RTT Early Data Support - ✅ COMPLETED
EndOfEarlyData
message (include/dtls/protocol/handshake.h
)EarlyDataExtension
struct)PreSharedKeyExtension
, PskKeyExchangeModesExtension
)EarlyDataReplayProtection
class)ConnectionState
values: EARLY_DATA
, WAIT_END_OF_EARLY_DATA
, EARLY_DATA_REJECTED
)NewSessionTicket
message (include/dtls/protocol/handshake.h
)SessionTicketManager
class)send_early_data()
, can_send_early_data()
, etc.)derive_early_traffic_secret()
)ConnectionConfig
with early data settings)include/dtls/protocol/handshake.h
)// EndOfEarlyData message (RFC 9147 Section 4.2.10)
class EndOfEarlyData {
// No content - just indicates end of early data
};
// NewSessionTicket message (RFC 9147 Section 4.2.11)
class NewSessionTicket {
uint32_t ticket_lifetime_; // Lifetime in seconds
uint32_t ticket_age_add_; // Random value for obfuscation
std::vector<uint8_t> ticket_nonce_; // Unique per ticket
std::vector<uint8_t> ticket_; // Encrypted ticket data
std::vector<Extension> extensions_; // Extensions (like early_data)
};
// Early Data Extension Structure
struct EarlyDataExtension {
uint32_t max_early_data_size; // Maximum early data the server will accept
};
// Pre-Shared Key Extension
struct PreSharedKeyExtension {
std::vector<PskIdentity> identities;
std::vector<std::vector<uint8_t>> binders; // PSK binder values
};
// PSK Key Exchange Modes Extension
struct PskKeyExchangeModesExtension {
std::vector<PskKeyExchangeMode> modes; // PSK_KE, PSK_DHE_KE
};
include/dtls/protocol/early_data.h
)// Session ticket storage and management
class SessionTicketManager {
Result<NewSessionTicket> create_ticket(/* parameters */);
Result<SessionTicket> decrypt_ticket(const std::vector<uint8_t>& encrypted_ticket);
bool store_ticket(const std::string& identity, const SessionTicket& ticket);
std::optional<SessionTicket> get_ticket(const std::string& identity) const;
size_t cleanup_expired_tickets();
};
// Early data replay protection
class EarlyDataReplayProtection {
bool is_replay(const std::string& ticket_identity,
const std::vector<uint8_t>& early_data_hash);
void record_early_data(const std::string& ticket_identity,
const std::vector<uint8_t>& early_data_hash);
size_t cleanup_old_entries();
};
// Early data state tracking
enum class EarlyDataState : uint8_t {
NOT_ATTEMPTED = 0, // No early data attempted
SENDING = 1, // Client sending early data
ACCEPTED = 2, // Server accepted early data
REJECTED = 3, // Server rejected early data
COMPLETED = 4 // Early data phase completed
};
// Connection states extended for early data
enum class ConnectionState : uint8_t {
// ... existing states ...
EARLY_DATA = 11, // Client sending early data
WAIT_END_OF_EARLY_DATA = 12, // Server waiting for EndOfEarlyData
EARLY_DATA_REJECTED = 13 // Early data was rejected by server
};
include/dtls/connection.h
)class Connection {
public:
// Early data transmission
Result<void> send_early_data(const memory::ZeroCopyBuffer& data);
bool can_send_early_data() const;
bool is_early_data_accepted() const;
bool is_early_data_rejected() const;
// Session ticket management
Result<void> store_session_ticket(const protocol::NewSessionTicket& ticket);
std::vector<std::string> get_available_session_tickets() const;
void clear_session_tickets();
// Statistics
struct EarlyDataStats {
size_t bytes_sent = 0;
size_t bytes_accepted = 0;
size_t bytes_rejected = 0;
std::chrono::milliseconds response_time{0};
bool was_attempted = false;
};
EarlyDataStats get_early_data_stats() const;
};
struct ConnectionConfig {
// Early data support
bool enable_early_data = false;
size_t max_early_data_size = 16384; // 16KB
// Early data configuration
std::chrono::milliseconds early_data_timeout{5000};
bool allow_early_data_replay_protection{true};
};
enum class ConnectionEvent : uint8_t {
// ... existing events ...
EARLY_DATA_ACCEPTED, // Server accepted early data
EARLY_DATA_REJECTED, // Server rejected early data
EARLY_DATA_RECEIVED, // Data received during early data phase
NEW_SESSION_TICKET_RECEIVED // New session ticket for future 0-RTT
};
// Configure connection for early data
ConnectionConfig config;
config.enable_early_data = true;
config.max_early_data_size = 16384;
// Create client connection
auto connection = Connection::create_client(config, crypto_provider, server_address);
// Start handshake
connection->start_handshake();
// Send early data (if available session ticket exists)
if (connection->can_send_early_data()) {
memory::ZeroCopyBuffer early_data = create_http_request();
auto result = connection->send_early_data(early_data);
if (result.is_success()) {
std::cout << "Early data sent successfully\n";
}
}
// Check early data status
if (connection->is_early_data_accepted()) {
std::cout << "Server accepted early data\n";
} else if (connection->is_early_data_rejected()) {
std::cout << "Server rejected early data - will retry after handshake\n";
}
// Store received session tickets for future use
connection->set_event_callback([&](ConnectionEvent event, const std::vector<uint8_t>& data) {
if (event == ConnectionEvent::NEW_SESSION_TICKET_RECEIVED) {
// Parse and store the session ticket
auto ticket = parse_session_ticket(data);
connection->store_session_ticket(ticket);
std::cout << "Session ticket stored for future 0-RTT\n";
}
});
// Server configuration for early data
ConnectionConfig server_config;
server_config.enable_early_data = true;
server_config.max_early_data_size = 8192; // More conservative server limit
auto server_connection = Connection::create_server(server_config, crypto_provider, client_address);
// Handle early data events
server_connection->set_event_callback([&](ConnectionEvent event, const std::vector<uint8_t>& data) {
if (event == ConnectionEvent::EARLY_DATA_RECEIVED) {
// Process early data from client
process_early_application_data(data);
}
});
include/dtls/protocol/
├── handshake.h # Extended with EndOfEarlyData, NewSessionTicket, extensions
└── early_data.h # New: Session management and replay protection
src/protocol/
├── handshake.cpp # Extended with new message implementations
└── early_data.cpp # New: Session and replay protection implementation
include/dtls/
├── connection.h # Extended with early data API
└── types.h # Extended with new connection states
examples/
└── early_data_example.cpp # Comprehensive usage example
docs/
└── EARLY_DATA_IMPLEMENTATION.md # This document
examples/early_data_example.cpp
This implementation provides the foundation for RFC 9147 Section 4.2.10 compliance:
The implementation provides a solid foundation for 0-RTT early data support in DTLS v1.3, with room for production hardening and advanced features.