Implementation Considerations (Informative)

Status of This Appendix

This appendix is informative and non-normative. It provides guidance, rationale, and illustrative examples for implementing PALISADE securely, but it does not define protocol behavior or compliance requirements. Conformance to the PALISADE specification does not depend on following any particular implementation technique described in this appendix.


X.1 Secure Handling of Cryptographic Key Material

PALISADE's security properties, including forward secrecy and replay resistance, depend on the correct handling and timely disposal of cryptographic key material. While the core specification mandates that expired or superseded secrets be erased, the mechanisms used to achieve secure erasure are inherently implementation-dependent.

Implementations are encouraged to follow the general principles below:

  • Minimize the lifetime of sensitive key material.
  • Avoid unnecessary copying of secrets.
  • Separate long-term keys from short-lived session keys.
  • Prefer deriving keys on demand rather than caching derived material.
  • Treat intermediate secrets (e.g., KEM shared secrets, handshake secrets) as highly sensitive.

The remainder of this appendix discusses common implementation strategies and tradeoffs.


X.2 Language Memory Models and Erasure Guarantees

Programming languages differ significantly in how memory is allocated, reused, and reclaimed. These differences affect the strength of guarantees that can be made about key erasure.

X.2.1 Explicit-Memory Languages

Examples include C, C++, Rust, Zig, and similar languages that provide explicit control over memory allocation and deallocation.

In these environments, implementations can provide strong guarantees regarding key erasure by:

  • Overwriting sensitive buffers immediately after use.
  • Preventing compiler optimizations from eliding zeroization operations.
  • Avoiding reuse of memory containing key material without explicit clearing.
  • Optionally locking memory pages to reduce exposure to swapping.

When implemented carefully, these approaches allow immediate and deterministic destruction of sensitive key material.

X.2.2 Garbage-Collected Languages

Examples include Go, Java, Swift, Python, JavaScript, and other languages with automatic memory management.

In garbage-collected environments:

  • Explicit overwriting of buffers reduces exposure but does not guarantee immediate memory reuse.
  • Memory containing key material may remain in process memory until garbage collection occurs.
  • Swap or crash dumps may retain remnants of sensitive data.

Despite these limitations, garbage-collected implementations can still achieve acceptable security by:

  • Explicitly overwriting key buffers before dereferencing them.
  • Limiting the scope and lifetime of objects containing secrets.
  • Avoiding long-lived global structures for key storage.
  • Using runtime barriers or language-specific techniques to prevent premature optimization.

These environments provide best-effort erasure, which is sufficient for many deployment contexts when appropriately documented.


X.3 Illustrative Key Erasure Techniques

The following examples illustrate common approaches used to reduce the lifetime of sensitive key material. These examples are provided for educational purposes only and do not mandate any specific implementation strategy.

X.3.1 Example: C / C++

// Illustrative example only

#include <string.h>

void erase_key(uint8_t *key, size_t len) {
    explicit_bzero(key, len);
}

This approach relies on a zeroization primitive that is guaranteed not to be optimized away by the compiler.

X.3.2 Example: Rust

use zeroize::Zeroize;

// Illustrative example only

fn erase_key(key: &mut [u8]) {
    key.zeroize();
}

The zeroize crate provides compiler-resistant zeroization for sensitive data structures.

X.3.3 Example: Go

// Illustrative example only

import "runtime"

func eraseKey(key []byte) {
    for i := range key {
        key[i] = 0
    }
    runtime.KeepAlive(key)
}

The runtime.KeepAlive call reduces the likelihood that the compiler will reorder or elide the zeroization.


X.4 Packet Format Implementation Guidance

Byte Order: All multi-byte integers MUST be encoded in big-endian (network byte order).

Length Fields: All length fields are in bytes and represent the length of the immediately following data.

Reserved Fields: All reserved fields MUST be set to zero by senders and SHOULD be ignored by receivers (forward compatibility).

Padding: No padding or alignment is used in any structure. Variable-length fields are length-prefixed.

Validation: Implementations MUST validate:

  • Length fields do not cause buffer overruns
  • Reserved bits/fields are zero
  • Enum values are within defined ranges
  • Total message length matches expected size
  • Magic number matches expected value (0x5150)
  • Version is supported (0x10 or 0x11)

Error Handling: Implementations SHOULD reject malformed packets immediately and log errors for diagnostic purposes. Do not attempt to parse beyond known-good boundaries.


X.5 Traffic Shaping Implementation (Non-Normative)

This section provides implementation guidance and reference strategies for realizing the traffic shaping modes defined in Sections 10.1–10.4. The material in this section is non-normative and does not impose specific architectural or scheduling requirements, provided that the externally observable behavior conforms to the protocol requirements.

Traffic shaping affects packet generation only. It does not alter cryptographic processing, nonce construction, replay protection, or packet validation rules.

X.5.1 General Implementation Principles

All traffic shaping modes MUST preserve the following invariants:

  • All packets (real or dummy) use identical wire format and encryption.
  • Nonce construction, replay protection, and epoch handling are unchanged.
  • Dummy packets MUST be indistinguishable from real packets prior to successful AEAD decryption.
  • Receivers MUST authenticate packets before inspecting any traffic-shaping metadata.
  • Traffic shaping logic SHOULD be implemented as a local transmission policy layer, independent of cryptographic state machines.

X.5.2 Mode 1 (Light Padding) — Reference Strategy

A typical Mode 1 implementation applies randomized padding on a per-packet basis:

payload_len = len(application_payload)
padding_len = uniform_random(pad_min, pad_max)
padding = crypto_random_bytes(padding_len)

encrypted_header.padding_len = padding_len
plaintext = encrypted_header || application_payload || padding

Implementation Notes

  • Padding bytes SHOULD be generated using a cryptographically secure RNG.
  • Padding MUST NOT contain predictable patterns or reused buffers.
  • Receivers MUST ignore padding bytes entirely after authenticated decryption.
  • Implementations MAY tune pad_min / pad_max dynamically based on MTU or traffic class.
  • Mode 1 is intentionally stateless and low-overhead.

X.5.3 Mode 2 (Constant-Rate) — Architectural Pattern

Mode 2 implementations typically separate concerns into:

  • Traffic scheduler (controls send timing)
  • Packet generator (real vs dummy)
  • Bandwidth governor (prevents runaway transmission)

The protocol does not mandate a specific scheduler design. One common strategy is slot-based scheduling, described below.

X.5.4 Slot-Based Scheduling (Illustrative)

A slot-based scheduler divides time into fixed or jittered intervals ("slots") and ensures that at most one packet is emitted per slot.

slot_duration = slot_duration_ms ± random_jitter
packets_per_slot = 1

For each slot:
    If real data is available, send a real packet.
    Otherwise, send a dummy packet (subject to bandwidth constraints).

This strategy ensures a stable packet emission rate without delaying real traffic.

X.5.5 Dummy Packet Generation

Dummy packets SHOULD be generated as follows:

  • Payload length equals the selected bucket size.
  • Payload contents are cryptographically random.
  • Header fields (epoch, seq, nonce, etc.) are valid and monotonic.
  • A dummy indicator flag MAY be set inside the encrypted header.

Dummy packets MUST:

  • Use valid nonces and sequence numbers.
  • Participate fully in replay protection.
  • Be indistinguishable from real packets prior to decryption.

Receivers MUST authenticate dummy packets before discarding them.

X.5.6 Bucket Selection Strategies

Mode 2 supports multiple bucket strategies:

Single Fixed Size

  • All packets padded to a constant size (e.g., 1200 bytes).
  • Simplest and most predictable.

Fixed-Size Buckets

  • A small set of allowed sizes (e.g., 512, 1024, 1500).
  • Real packets are padded up to the nearest bucket.
  • Dummy packets randomly select a bucket.

Low-Variation Buckets

  • Each bucket permits ±V bytes of random variation.
  • Improves resistance to exact size fingerprinting.
  • Variation MUST be cryptographically random.

Implementations MUST reject plaintexts that exceed the selected bucket size.

X.5.7 Bandwidth and DoS Protection

To prevent amplification and resource exhaustion, implementations SHOULD enforce:

  • A maximum total bandwidth cap (bytes/sec).
  • A cap on dummy packet generation rate.
  • Priority for real traffic over dummy traffic.

One possible policy:

if real_traffic_rate >= target_rate:
    send no dummy packets
else:
    send min(required_dummy_packets, bandwidth_cap)

Dummy packet generation SHOULD be suppressed if bandwidth caps are reached.

X.5.8 Reference Pseudocode (Illustrative)

class ConstantRateShaper:
    def __init__(self, config):
        self.slot_rate = config.slot_rate_packets_per_sec
        self.slot_duration = config.slot_duration_ms / 1000
        self.max_bandwidth = config.max_bandwidth_bytes_per_sec

        self.bytes_sent = 0
        self.window_start = now()

    def send(self, packet, is_dummy=False):
        if now() - self.window_start > 1.0:
            self.bytes_sent = 0
            self.window_start = now()

        if self.bytes_sent + len(packet) > self.max_bandwidth:
            if is_dummy:
                return False
        transmit(packet)
        self.bytes_sent += len(packet)
        return True

This example illustrates rate bounding, not required logic.

X.5.9 Operational Considerations

  • Mode switching SHOULD be rate-limited to avoid traffic anomalies.
  • Configuration changes SHOULD take effect at epoch boundaries.
  • Logging of dummy packet behavior SHOULD be rate-limited to avoid leaks.
  • High-security modes significantly increase bandwidth usage and power consumption.

X.5.10 Security Summary

Correct implementation of traffic shaping:

  • Reduces size and timing metadata leakage
  • Does not weaken cryptographic guarantees
  • Preserves replay protection and nonce uniqueness
  • Remains robust under packet loss and reordering

Traffic shaping is defense-in-depth, not a substitute for encryption or authentication.


X.6 Rekeying Implementation (Non-Normative)

This section provides implementation guidance for rekeying and epoch transitions in PALISADE. It is non-normative and does not alter or supersede the protocol requirements defined in Section 11.

Its purpose is to help implementers build correct, safe, and efficient rekeying logic that conforms to the protocol's security properties.

X.6.1 Rekeying Overview

Rekeying transitions a session from epoch n to epoch n+1 while preserving tunnel continuity.

Implementations typically consist of:

  • Rekey trigger detection
  • Epoch key derivation
  • Controlled epoch transition
  • Temporary overlap handling
  • Secure key erasure

X.6.2 Rekey Triggers (Implementation Guidance)

Implementations commonly initiate rekeying when one or more of the following conditions are met:

  • Sequence counter approaches exhaustion (see Section 9)
  • Configured time interval elapses (e.g., every 60 seconds)
  • Configured data volume threshold is exceeded
  • A CTRL_REKEY control frame is received

Typical implementations centralize rekey trigger checks in the packet send path to ensure timely transitions.

X.6.3 Epoch Transition State Tracking

A practical implementation models rekeying as a short-lived transition state:

EPOCH_NORMAL (n)
    |
    | rekey triggered
    v
EPOCH_TRANSITION (n → n+1)
    |
    | overlap window expires
    v
EPOCH_NORMAL (n+1)

During transition:

  • New packets are sent using epoch n+1 only
  • Old epoch n keys remain available for decryption only
  • Overlap duration and out-of-order limits are enforced

X.6.4 Deriving and Activating a New Epoch

A typical rekey initiation flow:

def initiate_rekey():
    # Derive next epoch secret
    epoch_n1_secret = hkdf_expand(epoch_n_secret, b"epoch step", 32)

    # Derive traffic keys for new epoch
    epoch_n1_keys = derive_traffic_keys(epoch_n1_secret)

    # Atomically switch transmit state
    epoch_id += 1
    seq = 0
    current_epoch = epoch_n1

    # Start overlap timer
    overlap_expiry = now() + overlap_duration_ms

    # Send CTRL_REKEY frame
    send_ctrl_rekey(epoch_id)

Key points:

  • Epoch increment and sequence reset occur atomically
  • No packet is sent with mixed epoch/sequence state
  • Old keys are retained only for decryption

X.6.5 Decryption During Epoch Overlap

During the overlap window, receivers may accept packets from two epochs.

A common decryption strategy:

def decrypt_packet(packet):
    # Fast path: current epoch
    pt = try_decrypt(packet, current_epoch.keys)
    if pt:
        return pt

    # Overlap path: previous epoch
    if overlap_active():
        pt = try_decrypt(packet, old_epoch.keys)
        if pt and within_out_of_order_window(packet):
            return pt

    return None  # reject

Notes:

  • Always try the current epoch first
  • Old epoch packets must still pass replay checks
  • Overlap does not relax replay protection rules

X.6.6 Out-of-Order Packet Tracking

Implementations typically maintain a bounded structure to track old-epoch packets during overlap.

Example approach:

class OutOfOrderTracker:
    def __init__(self, max_ooo):
        self.max_ooo = max_ooo
        self.seen = set()

    def accept(self, seq, min_allowed_seq):
        if seq < min_allowed_seq:
            return False
        if seq in self.seen:
            return False
        self.seen.add(seq)
        return True

Guidance:

  • Track only old-epoch packets
  • Enforce max_out_of_order_packets
  • Garbage-collect tracking state periodically
  • Do not reuse this tracker outside overlap

X.6.7 Overlap Timer Handling

When the overlap window expires:

def expire_overlap():
    zeroize(old_epoch.secret)
    zeroize(old_epoch.keys)
    old_epoch = None

Implementations should:

  • Use a monotonic clock for overlap timers
  • Ensure key erasure occurs exactly once
  • Prevent race conditions between decryption and erasure

X.6.8 Secure Key Erasure

Recommended practices:

  • Zero epoch secrets immediately after they become obsolete
  • Zero traffic keys and IVs together
  • Use compiler-safe zeroing primitives
  • Avoid keeping old keys in long-lived objects

For garbage-collected languages, explicitly overwrite buffers before dereferencing.

X.6.9 Error Handling During Rekeying

If rekeying fails at any point:

  • Abort the session
  • Erase all partially derived keys
  • Do not attempt to recover mid-transition
  • Require a full handshake for recovery

Partial or inconsistent epoch state is unsafe.

X.6.10 Implementation Pitfalls to Avoid

Common mistakes:

  • Sending packets with old epoch after rekey
  • Accepting old epoch packets after overlap expiry
  • Forgetting to reset sequence counter on epoch increment
  • Treating overlap as optional replay relaxation
  • Failing to erase old keys promptly

X.6.11 Summary

Correct rekeying implementations:

  • Are atomic
  • Are bounded in time and packet count
  • Preserve replay guarantees
  • Enforce strict key lifetimes
  • Never reuse (epoch_id, seq) pairs

When implemented correctly, rekeying provides strong forward secrecy without disrupting tunnel operation.


X.7 Session Resumption Implementation (Non-Normative)

This appendix provides implementation guidance for PALISADE session resumption and 0-RTT early data. It supplements the normative requirements in Section 12 and is non-authoritative.

Nothing in this appendix relaxes or overrides protocol requirements.

X.7.1 Resumption Ticket Data Structures

X.7.1.1 TicketInner (Logical Structure)

Before encryption, the inner ticket contains the following fields:

struct TicketInner {
    u128 ticket_id;          // Unique identifier (single-use)
    u32  issued_at;          // UNIX timestamp (seconds)
    u32  expires_at;         // UNIX timestamp (seconds)
    u8   max_early_data;     // Maximum 0-RTT data in KB (0 = disabled)
    opaque psk[32];          // Resumption pre-shared key
    opaque client_id[16];    // Optional stable client identifier
    opaque session_id[16];   // Original session identifier
}

Total size: 89 bytes (fixed)

Implementation notes:

  • ticket_id MUST be unpredictable and collision-resistant
  • psk MUST be generated from cryptographically secure randomness
  • All fields MUST be serialized deterministically before encryption

X.7.2 Ticket Encryption and Authentication

X.7.2.1 Ticket Encryption

Servers encrypt TicketInner using AEAD:

encrypted_inner = AEAD_Encrypt(
    key   = ticket_secret,
    nonce = random_nonce[12],
    aad   = label("ticket"),
    pt    = TicketInner
)

Implementation guidance:

  • ticket_secret should be rotated periodically (see Section 6)
  • nonce MUST be generated using a CSPRNG
  • aad MUST be constant and protocol-specific

X.7.2.2 Ticket Authentication

Each ticket is authenticated with a server signature:

ticket_hash = SHA-3-256(nonce || encrypted_inner)
signature   = SIG_server_privkey(ticket_hash)

Implementation notes:

  • The signature protects against ticket forgery
  • The hash MUST be computed before signature generation
  • Signature verification MUST precede ticket decryption on the server

X.7.3 Resumption Handshake Processing

X.7.3.1 Server Processing Flow

A typical resumption server flow:

  1. Verify server_signature on ResumeTicket
  2. Decrypt encrypted_inner using ticket_secret
  3. Validate TicketInner fields:
    • expires_at > current_time
    • ticket_id not previously used
  4. Record ticket_id as used (replay cache)
  5. Perform fresh KEM exchange using K'_c
  6. Derive resumption master secret
  7. Issue ServerHelloResume

Implementation guidance:

  • Ticket replay checks MUST occur before key derivation
  • Ticket validation failures SHOULD be indistinguishable on the wire
  • All intermediate secrets SHOULD be erased immediately on failure

X.7.4 Resumption Key Derivation (Reference)

The resumption key schedule combines a PSK with a fresh KEM shared secret using a sequential HKDF-Extract combiner, aligned with TLS 1.3.

def derive_resumption_master_secret(
    psk: bytes,
    ss_prime_c: bytes,
    resume_nonce: bytes,
    server_nonce_prime: bytes,
    transcript_hash: bytes
) -> bytes:
    # Combine PSK and fresh KEM secret
    ikm1 = HKDF_Extract(zeros(32), psk)
    ikm2 = HKDF_Extract(ikm1, ss_prime_c)

    # Mix in nonces
    ikm = ikm2 || resume_nonce || server_nonce_prime

    # Extract early secret
    early_secret = HKDF_Extract(zeros(32), ikm)

    # Bind transcript
    handshake_secret = HKDF_Expand(
        early_secret,
        label("handshake secret") || transcript_hash,
        32
    )

    # Derive resumption master secret
    return HKDF_Expand(
        handshake_secret,
        label("master secret"),
        32
    )

Notes:

  • This construction ensures forward secrecy even if the PSK leaks
  • Transcript binding prevents cross-session key confusion
  • Implementations SHOULD reuse the same HKDF utilities as the full handshake

X.7.5 0-RTT Early Data Implementation

X.7.5.1 Early Data Key Derivation

If max_early_data > 0:

early_data_key = HKDF-Expand(resumption_secret, label("early data key"), 32)
early_data_iv  = HKDF-Expand(resumption_secret, label("early data iv"), 12)

Implementation notes:

  • Early data keys MUST be distinct from application traffic keys
  • Early data MUST use an independent nonce sequence
  • Early data MUST NOT reuse epoch/sequence state

X.7.5.2 Early Data Safety Enforcement

Applications MUST enforce replay-safe behavior for early data.

Examples of safe early data usage:

  • Read-only queries
  • Cache fetches
  • Idempotent requests with deduplication tokens

Examples of unsafe early data usage:

  • State mutation
  • Financial transactions
  • Authentication or authorization changes

Servers SHOULD:

  • Reject early data exceeding max_early_data
  • Log early data rejection events (rate-limited)
  • Disable early data for sensitive endpoints

X.7.6 Ticket Replay Cache Implementation

X.7.6.1 Replay Cache Semantics

Servers track used ticket_id values to enforce single-use tickets.

Requirements:

  • Each ticket_id MUST be accepted at most once
  • Cache entries MUST expire automatically
  • Cache MUST be resilient to DoS amplification

X.7.6.2 Lifetime Management

Recommended defaults:

ParameterValue
Minimum retention5 minutes
Default retention1 hour
Maximum retention24 hours

Implementation strategies:

  • Time-indexed hash maps
  • Sharded replay caches
  • Periodic cleanup (≥ once per minute)

Example structure:

class TicketReplayCache:
    def __init__(self):
        self.used = {}  # ticket_id -> expiry_time

    def mark_used(self, ticket_id, expiry):
        self.used[ticket_id] = expiry

    def is_used(self, ticket_id):
        return ticket_id in self.used

    def cleanup(self, now):
        self.used = {
            tid: exp for tid, exp in self.used.items()
            if exp > now
        }

X.7.7 Operational Considerations

Logging

  • Replay attempts SHOULD be logged
  • Ticket validation failures SHOULD be rate-limited

Key Hygiene

  • ticket_secret SHOULD be rotated (see Section 6)
  • Old ticket secrets MUST be erased after rotation
  • Resumption secrets MUST be erased after session establishment

Deployment Guidance

  • Disable early data for admin or control-plane APIs
  • Monitor replay cache growth
  • Treat replay cache exhaustion as a security event

X.7.8 Security Summary

When implemented correctly, PALISADE resumption provides:

  • Strong replay resistance (single-use tickets)
  • Forward secrecy (fresh PQ KEM per resumption)
  • Context separation (domain-separated KDF labels)
  • TLS-grade 0-RTT semantics with explicit constraints

X.8 Migration Implementation (Non-Normative)

This appendix provides implementation guidance for PALISADE migration and roaming. It is non-normative and does not define wire formats or protocol requirements. All normative behavior is specified in Section 13.

Migration is a best-effort optimization, not a transport guarantee. Implementations should favor simplicity, correctness, and security over aggressive recovery behavior.

X.8.1 Migration State Model

Implementations SHOULD maintain an explicit per-session migration state machine:

IDLE → IN_PROGRESS → COMPLETED → IDLE

Recommended states:

IDLE

No migration activity.

IN_PROGRESS

A migration has been accepted and address rebinding is underway.

COMPLETED

Migration has completed successfully; state will reset to IDLE.

Migration state MUST be scoped per session and per direction.

X.8.2 Soft Migration Processing Flow

Soft migration is initiated by receiving a valid CTRL_MIGRATE control frame within an authenticated encrypted packet.

Recommended Processing Steps

  1. Verify packet authenticity and decrypt
  2. Validate epoch_id == current_epoch
  3. Apply epoch/sequence replay checks
  4. Verify migration reason normalization
  5. Check migration state (must not already be IN_PROGRESS)
  6. Update peer address binding (5-tuple)
  7. Mark migration as COMPLETED

Address Rebinding

When a migration is accepted:

  • The implementation SHOULD bind the session to the observed source address of the authenticated packet.
  • No cryptographic material is changed.
  • Existing replay windows and sequence counters remain intact.

X.8.3 Concurrency Handling

Migration is inherently stateful and must be serialized.

Recommended behavior:

  • Only one migration per session may be processed at a time.
  • If a migration is already IN_PROGRESS:
    • Additional migration requests SHOULD be rejected or ignored.
    • Queuing migration requests is discouraged due to complexity.
  • Atomicity is more important than throughput.

X.8.4 Rate Limiting (DoS Protection)

Implementations SHOULD apply rate limiting to migration attempts to prevent abuse.

Suggested strategy:

  • Maintain a sliding time window per session.
  • Track the number of accepted migration attempts.
  • Reject or ignore migration requests exceeding policy thresholds.

Example (illustrative only):

  • Max migrations per session: 10
  • Time window: 60 seconds

Exact thresholds are implementation-defined and SHOULD be configurable.

X.8.5 Replay and Duplication Defense

Migration control frames are already protected by:

  • AEAD authentication
  • Epoch/sequence anti-replay window

No additional cryptographic replay mechanism is required.

However, implementations MAY add lightweight safeguards:

  • Track the most recent migration attempt
  • Reject immediate duplicate migration frames
  • Avoid reprocessing identical migration events

These measures are optional and should not weaken correctness.

X.8.6 Migration Timeout Handling

Implementations SHOULD ensure migration processing completes promptly.

Recommended behavior:

  • Set a migration processing timeout (e.g., 3–5 seconds).
  • If migration does not complete within the timeout:
    • Reset migration state to IDLE
    • Retain the previous address binding

Timeouts prevent sessions from becoming stuck in partial migration states.

X.8.7 Bidirectional Migration

PALISADE permits simultaneous bidirectional migration:

  • Client and server may independently migrate their sending paths.
  • Each direction SHOULD track migration state independently.
  • Address rebinding applies only to the peer that initiated migration.

Implementations MUST avoid assuming migration symmetry.

X.8.8 Interaction with Rekeying

Migration and rekeying are orthogonal:

  • Soft migration MUST NOT derive new keys.
  • Rekeying MUST occur only via CTRL_REKEY or configured triggers.
  • Epoch overlap logic remains unchanged by migration.

If a migration coincides with an epoch transition:

  • Rekeying semantics take precedence.
  • Migration address rebinding remains valid across epochs.

X.8.9 Failure Handling and Fallback

If soft migration fails:

  • The session SHOULD continue using the previous address binding if possible.
  • If packets can no longer be exchanged:
    • Hard migration (session resumption) SHOULD be attempted.
  • Migration failures MUST NOT corrupt cryptographic state.

X.8.10 Logging and Telemetry (Optional)

Implementations MAY log migration events for diagnostics and monitoring:

  • Migration attempts
  • Success or failure
  • Rate-limit rejections
  • Address changes

Logs SHOULD NOT include sensitive material or unencrypted packet contents.

X.8.11 Security Summary

Implementation choices SHOULD preserve the following properties:

  • No unauthenticated migration
  • No replay-driven state changes
  • No migration-triggered key reuse
  • No distinguishable migration causes on the wire
  • No unbounded resource consumption

Migration should always degrade safely.


X.9 Error Handling Implementation (Non-Normative)

This appendix provides implementation guidance for handling PALISADE protocol errors. It supplements Section 15 by offering recommended patterns for error classification, logging, disclosure control, and operational robustness. Nothing in this appendix alters the normative behavior defined in Section 15.


X.9.1 Design Goals

An error handling implementation SHOULD aim to:

  • Fail closed on cryptographic or protocol integrity violations
  • Avoid creating side-channels or distinguishable error oracles
  • Bound resource usage under error conditions
  • Provide operators with sufficient diagnostics without exposing sensitive data
  • Maintain forward compatibility with future error codes

X.9.2 Centralized Error Handling

Implementations SHOULD route all protocol errors through a centralized error handler rather than scattering error logic throughout the codebase.

A centralized handler simplifies:

  • Consistent fatal vs. non-fatal handling
  • Uniform logging and metrics
  • Disclosure policy enforcement
  • Rate limiting of error responses

X.9.3 Error Classification (Implementation View)

Although Section 15 defines fatal vs. non-fatal semantics, implementations often benefit from an additional disclosure classification:

LevelDescriptionWire Behavior
PublicSafe to discloseSend error code and generic message
LimitedPotentially sensitiveSend error code with generic text only
InternalDangerous to discloseDo not send; log locally only

Note: Disclosure level does not override fatality. A fatal error may still be internal-only.


X.9.4 Error Response Generation

When to Send an Error Response

An implementation MAY send an error response if:

  • The session or handshake state is still valid
  • Sending the response does not leak sensitive information
  • The response cannot be abused for amplification or probing

An implementation SHOULD suppress error responses when:

  • AEAD decryption fails
  • Replay is detected
  • The peer appears unauthenticated or malicious
  • Rate limits are exceeded

X.9.5 Logging Guidelines

Implementations SHOULD log all protocol errors locally with sufficient detail for diagnosis.

Recommended Log Fields

  • Timestamp
  • Error code
  • Error category
  • Session identifier (opaque)
  • Direction (ingress/egress)
  • Epoch and sequence (if applicable)
  • Local action taken (drop, terminate, continue)

Sensitive material (keys, nonces, raw ciphertext) MUST NOT be logged.


X.9.6 Metrics and Observability

Implementations MAY expose metrics such as:

  • Total errors by category
  • Fatal vs. non-fatal error counts
  • Error rates over time
  • Replay detections
  • Decryption failures

Metrics SHOULD be aggregated and MUST NOT expose per-packet identifiers.


X.9.7 Rate Limiting

Error responses SHOULD be rate-limited to prevent:

  • Reflection attacks
  • Amplification attacks
  • Error-based probing

Recommended strategies:

  • Token bucket per peer
  • Global error response cap per second
  • Silent drop once limits are exceeded

X.9.8 Error Handling Flow (Illustrative)

Example high-level flow:

receive_packet()
  ↓
parse_headers()
  ↓
decrypt_or_verify()
  ↓
if error:
    classify_error()
    log_error()
    if fatal:
        erase_keys()
        terminate_session()
    if disclosure_allowed:
        send_error_response()
    return

X.9.9 Duplicate and Replay Errors

For replayed or duplicate packets:

  • Implementations SHOULD drop silently
  • Implementations MAY increment replay counters
  • Implementations SHOULD NOT send error responses

This prevents replay detection from becoming a timing or oracle signal.


X.9.10 Interaction with Handshake Errors

During the handshake phase:

  • Some errors (e.g., version mismatch) may be safely reported
  • Others (e.g., signature failure) SHOULD use generic messages
  • Handshake errors SHOULD NOT reveal which specific check failed

X.9.11 Testing Recommendations

Implementations SHOULD include tests for:

  • Fatal error termination paths
  • Suppression of error responses on cryptographic failure
  • Rate-limited error generation
  • Replay detection without disclosure
  • Compatibility with unknown error codes

Unknown error codes SHOULD be treated as fatal unless explicitly specified otherwise.


X.9.12 Forward Compatibility

Implementations SHOULD:

  • Ignore unknown error codes safely
  • Log unknown codes for analysis
  • Avoid assuming contiguous or complete error code ranges

This ensures interoperability with future PALISADE versions.


X.9.13 Summary

This appendix provides guidance for:

  • Safe, consistent error handling
  • Preventing side-channel leakage
  • Maintaining operational observability
  • Supporting robust, production-grade deployments

The authoritative protocol behavior remains defined in Section 15.


X.10 Control Frame Implementation (Non-Normative)

This appendix provides implementation guidance for processing PALISADE control frames. It does not define wire formats or mandatory protocol behavior. All normative requirements are specified in Section 14.

X.10.1 Control Frame Handling Overview

Control frames are carried inside encrypted packets and are indistinguishable from data packets on the wire until decryption.

Recommended processing pipeline:

  1. Decrypt packet using normal AEAD path
  2. Inspect PublicHeader.flags
  3. If control_frame bit set:
    • parse payload as ControlFrame
    • dispatch by ctrl_type
  4. Apply control-specific logic

Control frames benefit from the same authentication, confidentiality, and replay protection as normal packets.

X.10.2 Control Frame Dispatch Model

Implementations SHOULD dispatch control frames through a centralized handler:

def handle_control_frame(frame):
    if frame.type == CTRL_REKEY:
        handle_rekey(frame)
    elif frame.type == CTRL_MIGRATE:
        handle_migration(frame)
    elif frame.type == CTRL_CLOSE:
        handle_close(frame)
    elif frame.type == CTRL_ACK:
        handle_ack(frame)
    else:
        ignore_or_log(frame)

Unknown control types SHOULD be ignored safely unless explicitly enabled.

X.10.3 Optional Control Acknowledgments (ACKs)

Control-frame acknowledgments are optional and intended for deployments that require:

  • Reliable control delivery
  • Auditable state transitions
  • High-loss or unstable networks

ACK support SHOULD be configurable per deployment.

X.10.4 Control Frame Sequencing (ACK-Enabled Mode)

When ACKs are enabled, implementations SHOULD:

  • Maintain a separate control-frame sequence counter
  • Use a 64-bit monotonically increasing counter
  • Increment once per transmitted control frame
  • Never wrap the counter

Sequence counters are per-session and per-direction.

X.10.5 Pending-Frame Tracking

ACK-enabled implementations SHOULD maintain a pending-frame table:

PendingFrame {
    ctrl_type
    sequence_number
    send_timestamp
    retry_count
}

This table enables retransmission and duplicate suppression.

X.10.6 ACK Sender State Machine (Recommended)

Send Control Frame
      ↓
Insert into pending table
      ↓
Start retransmission timer
      ↓
Wait for ACK / NAK / DEFER

Handling outcomes:

  • ACK → Remove from pending (success)
  • NAK → Remove from pending (failure)
  • DEFER → Extend timer, keep pending
  • Timeout → Retransmit (up to retry limit)

After exceeding retry limits, implementations SHOULD log failure and abandon the frame.

X.10.7 Duplicate Detection (Receiver Side)

To handle retransmissions safely, receivers SHOULD:

  • Track recently processed control-frame sequence numbers
  • Detect duplicates
  • Re-emit the original ACK if a duplicate is received

This ensures idempotent processing even under packet loss.

X.10.8 Retransmission Parameters (Recommended Defaults)

Suggested tuning values:

ParameterValue
Initial timeout2 seconds
Retries3
BackoffExponential
Max wait~14 seconds

These are guidelines, not protocol limits.

X.10.9 ACK Handling Logic (Illustrative)

def on_control_frame(frame):
    if is_duplicate(frame.seq):
        send_ack(frame, STATUS_ACK)
        return

    try:
        process(frame)
        mark_processed(frame.seq)
        send_ack(frame, STATUS_ACK)

    except ValidationError:
        send_ack(frame, STATUS_NAK)

    except BusyError:
        send_ack(frame, STATUS_DEFER)

X.10.10 Compatibility Considerations

Implementations SHOULD ensure:

  • ACK-enabled nodes accept non-sequenced control frames
  • Non-ACK nodes silently ignore CTRL_ACK frames
  • Mixed deployments interoperate without negotiation

ACK support MUST NOT be required for baseline protocol correctness.

X.10.11 Logging and Telemetry (Optional)

Implementations MAY log:

  • Control-frame sends
  • ACK / NAK / DEFER events
  • Retransmissions
  • Control-frame failures

Logs SHOULD be rate-limited and MUST NOT leak sensitive material.

X.10.12 Security Considerations

Implementation choices MUST preserve:

  • Replay safety (no double-processing)
  • Bounded resource use (no unbounded pending frames)
  • Idempotent state transitions
  • Resistance to ACK-based amplification attacks

ACKs improve reliability but increase complexity; deploy only where justified.


X.11 Ticket and Cookie Key Rotation Practices

Many deployments rotate ticket and cookie encryption keys periodically to reduce the impact of compromise and limit the validity window of replayable artifacts.

Common operational patterns include:

  • Maintaining a small number of active keys (e.g., current and previous).
  • Issuing new tickets or cookies using only the current key.
  • Accepting artifacts encrypted with either active key.
  • Securely erasing keys after a defined retention window.

Rotation intervals vary by deployment, but shorter intervals reduce exposure at the cost of increased operational complexity.


X.12 Deployment Profiles and Tradeoffs

Different deployment environments impose different security and operational constraints.

High-Assurance Deployments

  • Prefer explicit-memory languages for key-handling code.
  • Minimize reliance on garbage collection.
  • Isolate cryptographic operations where possible.
  • Favor deterministic key lifetimes and erasure.

Managed Runtime Deployments

  • Accept best-effort erasure guarantees.
  • Minimize key lifetime and object retention.
  • Avoid unnecessary serialization or logging of sensitive data.
  • Clearly document residual risks associated with memory management.

Hybrid Approaches

Some deployments combine approaches, using explicit-memory components for cryptographic operations and higher-level languages for protocol orchestration. This approach can balance strong security guarantees with development velocity.


X.13 Summary

This appendix provides practical guidance for implementing PALISADE securely across a range of programming environments. While the core specification defines required security properties, correct implementation requires careful attention to memory handling, key lifetime, and operational practices.

Implementers are encouraged to assess their deployment context and choose techniques that appropriately balance security, performance, and complexity.

PALISADE Protocol Specification Draft 00

INFORMATIONAL