Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paxe stack cluster tests #10

Merged
merged 10 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions trex-lib/src/main/java/com/github/trex_paxos/TrexApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

public class TrexApp<VALUE, RESULT> {

private class LeaderTracker {
protected class LeaderTracker {
volatile NodeId estimatedLeader = null;

void updateFromFixed(Fixed msg) {
Expand Down Expand Up @@ -65,13 +65,13 @@ void remove(UUID id) {
}
}

private final TrexEngine engine;
private final NetworkLayer networkLayer;
private final Function<VALUE, RESULT> serverFunction;
private final Supplier<ClusterMembership> clusterMembershipSupplier;
private final LeaderTracker leaderTracker = new LeaderTracker();
private final ResponseTracker<RESULT> responseTracker = new ResponseTracker<>();
private final Pickler<VALUE> valuePickler;
protected final TrexEngine engine;
protected final NetworkLayer networkLayer;
protected final Function<VALUE, RESULT> serverFunction;
protected final Supplier<ClusterMembership> clusterMembershipSupplier;
final protected LeaderTracker leaderTracker = new LeaderTracker();
final ResponseTracker<RESULT> responseTracker = new ResponseTracker<>();
protected final Pickler<VALUE> valuePickler;
public final NodeId nodeId;

public TrexApp(
Expand Down Expand Up @@ -218,7 +218,7 @@ public void stop() {

@SuppressWarnings("SameParameterValue")
@TestOnly
void setLeader(short i) {
protected void setLeader(short i) {
if (i == engine.nodeIdentifier()) {
engine.setLeader();
}
Expand Down
3 changes: 2 additions & 1 deletion trex-lib/src/main/java/com/github/trex_paxos/TrexEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Optional;
import java.util.concurrent.Semaphore;
import java.util.stream.Stream;

import static com.github.trex_paxos.TrexLogger.LOGGER;

/// The TrexEngine manages the timeout behaviours that surround the core Paxos algorithm.
Expand All @@ -36,7 +37,7 @@ public abstract class TrexEngine implements AutoCloseable {
public static final String THREAD_INTERRUPTED = "TrexEngine was interrupted awaiting the mutex probably to shutdown while under load.";

/// The underlying TrexNode that is the actual Part-time Parliament algorithm implementation guarded by this class.
final TrexNode trexNode;
final protected TrexNode trexNode;

final NodeId nodeId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

public class PickleMsg implements Pickler<TrexMessage> {
public static PickleMsg instance = new PickleMsg();

protected PickleMsg() {
}

private static final int HEADER_SIZE = 5; // fromNode(2) + toNode(2) + type(1)
private static final int BALLOT_NUMBER_SIZE = Integer.BYTES + 2; // counter(4) + nodeId(2)

Expand Down Expand Up @@ -301,8 +303,8 @@ private static BallotNumber readBallotNumber(ByteBuffer buffer) {
}

@Override
public byte[] serialize(TrexMessage cmd) {
return pickle(cmd);
public byte[] serialize(TrexMessage trexMessage) {
return pickle(trexMessage);
}

@Override
Expand Down
62 changes: 31 additions & 31 deletions trex-lib/src/test/java/com/github/trex_paxos/SimulationTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import com.github.trex_paxos.msg.BroadcastMessage;
import com.github.trex_paxos.msg.DirectMessage;
import com.github.trex_paxos.msg.TrexMessage;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

Expand All @@ -36,41 +35,42 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static com.github.trex_paxos.TrexLogger.LOGGER;
import static com.github.trex_paxos.Simulation.inconsistentFixedIndex;
import static com.github.trex_paxos.TrexLogger.LOGGER;
import static org.assertj.core.api.Assertions.assertThat;

public class SimulationTests {

@BeforeAll
static void setupLogging() {

final var logLevel = System.getProperty("java.util.logging.ConsoleHandler.level", "WARNING");
final Level level = Level.parse(logLevel);

LOGGER.setLevel(level);
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(level);
LOGGER.addHandler(consoleHandler);

// Configure SessionKeyManager logger
Logger sessionKeyManagerLogger = Logger.getLogger("");
sessionKeyManagerLogger.setLevel(level);
ConsoleHandler skmHandler = new ConsoleHandler();
skmHandler.setLevel(level);
sessionKeyManagerLogger.addHandler(skmHandler);

// Optionally disable parent handlers if needed
LOGGER.setUseParentHandlers(false);
sessionKeyManagerLogger.setUseParentHandlers(false);
}

@BeforeAll
static void setupLogging() {

final var logLevel = System.getProperty("java.util.logging.ConsoleHandler.level", "WARNING");
final Level level = Level.parse(logLevel);

LOGGER.setLevel(level);
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(level);
LOGGER.addHandler(consoleHandler);

// Configure SessionKeyManager logger
Logger sessionKeyManagerLogger = Logger.getLogger("");
sessionKeyManagerLogger.setLevel(level);
ConsoleHandler skmHandler = new ConsoleHandler();
skmHandler.setLevel(level);
sessionKeyManagerLogger.addHandler(skmHandler);

// Optionally disable parent handlers if needed
LOGGER.setUseParentHandlers(false);
sessionKeyManagerLogger.setUseParentHandlers(false);
}

@Test
public void testLeaderElection1000() {
RandomGenerator rng = Simulation.repeatableRandomGenerator(1234);
IntStream.range(0, 1000).forEach(i -> {
LOGGER.info("\n ================= \nstarting iteration: " + i);
testLeaderElection(rng);
LOGGER.info("\n ================= \nstarting iteration: " + i);
testLeaderElection(rng);
}
);
}
Expand Down Expand Up @@ -106,7 +106,7 @@ public void testLeaderElection(RandomGenerator rng) {
public void testClientWorkPerfectNetwork1000() {
RandomGenerator rng = Simulation.repeatableRandomGenerator(9876);
IntStream.range(0, 1000).forEach(i -> {
LOGGER.info("\n ================= \nstarting iteration: " + i);
LOGGER.info("\n ================= \nstarting iteration: " + i);
testClientWork(rng);
}
);
Expand Down Expand Up @@ -167,10 +167,10 @@ public void testClientWorkLossyNetwork1000() {

IntStream.range(0, 1000).forEach(i -> {
LOGGER.info("\n ================= \nstarting iteration: " + i);
final var minLogLength = testWorkLossyNetwork(rng);
if (minLogLength > maxOfMinimum.get()) {
maxOfMinimum.set(minLogLength);
}
final var minLogLength = testWorkLossyNetwork(rng);
if (minLogLength > maxOfMinimum.get()) {
maxOfMinimum.set(minLogLength);
}
}
);

Expand Down
173 changes: 136 additions & 37 deletions trex-paxe/PAXE.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,142 @@

# PAXE Protocol Specification v0.9
# Paxe Protocol Documentation

## Overview
PAXE is a lightweight encrypted protocol for Paxos clusters supporting multiplexed channels over UDP. It provides 0-RTT resumption for established sessions and 1-RTT authentication for new sessions.

## Packet Format
Paxe implements authenticated encryption for Trex Paxos messages using AES-GCM. It supports both standard and Data
Encryption Key (DEK) modes for efficient large payload handling.

## Wire Format

### Message Header (8 bytes)

```
+--------+--------+--------+--------+--------+--------+--------+--------+
| fromId | toId | channel| length |
+--------+--------+--------+--------+--------+--------+--------+--------+
```

- fromId (2 bytes): Source node identifier
- toId (2 bytes): Destination node identifier
- channel (2 bytes): Communication channel identifier
- length (2 bytes): Payload length

### Standard Message Format

```
+----------------+--------+-----------+------------+-----------------+
| Header (8) | Flags | Nonce(12) | Payload | Auth Tag (16) |
+----------------+--------+-----------+------------+-----------------+
```

### DEK Message Format

```
[UDP Header]
[PAXE Header - 4 bytes]
From Node ID: 2 byte
To Node ID: 2 byte
Channel ID: 2 byte
[Encryption Header - 28 bytes]
Nonce: 12 bytes
Auth Tag: 16 bytes
[Encrypted Payload]
Type: 1 byte
Length: 4 bytes
Data: variable
+----------------+--------+-----------+------------+----------+--------+------------+-----------------+
| Header (8) | Flags | Nonce(12) | DEK Key(32)| DEKNonce | Length | Payload | Auth Tag (16) |
+----------------+--------+-----------+------------+----------+--------+------------+-----------------+
```

## Channels
- Channel 0: Reserved for PAXE consensus messages

## Session Keys
- Derived from SRP exchange between node pairs
- Key = HKDF(srp_shared_secret, "PAXE-V2", from_id | to_id)
- Same key used for both directions between node pair

## Message Flow
1. SRP authentication (1-RTT) if no session exists:
- Indicated by Auth Required flag
- Must complete before encrypted traffic
2. Encrypted stream communication (0-RTT) using channels
3. Automatic session resumption after network changes

## References
1. When large messages are fragmented:
- Each fragment includes Message ID for reassembly
- Receiving node must buffer and reorder fragments
- Complete message processed after last fragment received
2. Applications must handle timeout and cleanup of incomplete fragment sequences
### Flags Byte Structure

- Bit 0: DEK flag (0=standard, 1=DEK mode)
- Bit 1: Must be 0
- Bit 2: Must be 1
- Bits 3-7: Reserved

## Key Classes

### PaxeNetwork

Core networking implementation handling:

- Message encryption/decryption
- Network I/O
- Channel management
- Pending message buffering

### SessionKeyManager

Manages secure key exchange:

- Implements SRP (RFC 5054) handshakes
- Tracks active sessions
- Handles key derivation
- Maintains node verifiers

### Crypto

Encryption primitives:

- AES-GCM operations
- Nonce generation
- Buffer management
- DEK encryption logic

### NetworkTestHarness

Test infrastructure providing:

- Network simulation
- Node creation
- Key exchange verification
- Cluster membership management

## Security Properties

### Authentication

- Every packet includes GCM authentication tag
- All headers are authenticated (fromId, toId, channel)
- Failed authentication triggers SecurityException

### Confidentiality

- AES-256-GCM for all payloads
- Unique nonce per message
- DEK mode for large messages

### Key Exchange

- SRP v6a (RFC 5054) for initial authentication
- Session key derivation via HKDF
- Key confirmation through GCM tag validation

## Usage Guidelines

### Message Size

- Standard mode for messages < 64KB
- DEK mode automatically used for larger payloads
- Maximum UDP packet size: 65507 bytes

### Channel Management

- System channels (1-99) reserved
- Application channels start from 100
- Broadcast vs direct messaging support

### Key Lifecycle

- Session keys rotated on network partition
- Verifiers distributed via configuration
- Node IDs must be globally unique

## Error Handling

### Network Errors

- Failed sends queued for retry
- Messages rejected if no session key
- Automatic key re-establishment

### Crypto Failures

- SecurityException on auth failures
- Buffer overflow protection
- Connection teardown on key errors

## Test Support

- InMemoryNetwork for unit testing
- NetworkTestHarness for integration
- Logging levels follow JUL conventions
Loading
Loading