Skip to content

Latest commit

 

History

History
606 lines (455 loc) · 21.4 KB

File metadata and controls

606 lines (455 loc) · 21.4 KB

Lockstep Chat

A secure, end-to-end encrypted messaging application implementing the J-PAKE (Password Authenticated Key Exchange by Juggling) protocol for cryptographically secure communication without prior key distribution.

Academic Context

This project was developed as an academic exercise for CYBS 5041: Applied Cryptography at New Mexico Institute of Mining and Technology (New Mexico Tech) in Fall 2025. The implementation demonstrates practical application of advanced cryptographic protocols in a real-world context, serving both as a learning tool and a reference implementation for secure password-authenticated key exchange.

Educational Objectives

  • Practical implementation of the J-PAKE protocol (RFC 8236)
  • Understanding of zero-knowledge proofs in cryptographic systems
  • Experience with end-to-end encryption in distributed systems
  • Application of HKDF (SHA-256) for key separation and transcript binding
  • Integration of cryptographic primitives with modern web technologies

Overview

Lockstep Chat demonstrates a practical implementation of the J-PAKE protocol (RFC 8236) in a real-world messaging application. The system ensures that messages are end-to-end encrypted, with the server acting solely as a relay without access to passwords, session keys, or message content.

Features

  • Password-Authenticated Key Exchange: Secure key establishment using only a shared password
  • End-to-End Encryption: Messages encrypted with AES-256-GCM using derived session keys
  • Zero-Knowledge Proofs: Schnorr proofs prevent man-in-the-middle attacks
  • Perfect Forward Secrecy: Each session generates unique ephemeral keys
  • HKDF-based Key Separation: Separate keys for encryption, MAC, and key confirmation
  • Secure Password Handling: Passwords are immediately hashed and never stored in plaintext
  • Real-time Communication: WebSocket-based messaging with Socket.IO
  • Modern UI: Responsive design with light/dark theme support
  • Privacy by Design: Server has zero knowledge of passwords or message content

Architecture

System Components

┌──────────────┐         ┌──────────────┐         ┌──────────────┐
│   Client A   │◄────────┤    Server    ├────────►│   Client B   │
│   (React)    │         │  (Node.js)   │         │   (React)    │
└──────────────┘         └──────────────┘         └──────────────┘
       │                                                  │
       │                                                  │
       └───────────── E2E Encrypted Channel ──────────────┘
                    (Established via J-PAKE)

Technology Stack

Frontend

  • React 18.2.0
  • Socket.IO Client 4.6.0
  • React Icons 5.3.0
  • Web Crypto API
  • Modern CSS with CSS Variables

Backend

  • Node.js
  • Express 4.18.2
  • Socket.IO 4.6.0
  • Helmet 7.1 (security headers)
  • CORS middleware

Installation

Prerequisites

  • Node.js (v18 or higher)
  • npm (v6 or higher)

Quick Start

  1. Clone the repository:
git clone https://github.com/vermi/lockstep-chat.git
cd lockstep-chat
  1. Run the application with a single command:

macOS/Linux:

./start.sh

Windows:

start.bat

Cross-platform (Node.js):

node start.js

The startup script will:

  • Check for Node.js installation
  • Install dependencies automatically if needed
  • Start both server and client
  • Open your browser to the application

Manual Setup

If you prefer to run components separately:

  1. Install server dependencies:
cd server
npm install
  1. Install client dependencies:
cd ../client
npm install

Usage

Starting the Application

Using Startup Scripts (Recommended)

Simply run the appropriate startup script for your platform:

  • ./start.sh (macOS/Linux)
  • start.bat (Windows)
  • node start.js (Cross-platform)

Manual Start

  1. Start the server:
cd server
npm start

The server will run on http://localhost:3001

  1. Start the client (in a new terminal):
cd client
npm start

The client will open at http://localhost:3000

Establishing Secure Communication

  1. Open two browser windows at http://localhost:3000
  2. Enter the following in both windows:
    • Username: Unique identifier for each participant
    • Room ID: Same room identifier for both participants
    • Shared Password: Same password (this is never transmitted)
  3. Click "Join Room" in both windows
  4. The J-PAKE protocol will automatically establish a secure session
  5. Once connected, messages are end-to-end encrypted

Cryptographic Implementation

J-PAKE Protocol Flow

The implementation follows RFC 8236 specification:

Round 1

  1. Each party generates random exponents x1, x2
  2. Computes g1 = g^x1 and g2 = g^x2
  3. Generates Schnorr zero-knowledge proofs for x1 and x2
  4. Exchanges g1, g2 and proofs

Round 2

  1. Each party computes a combined generator
  2. Derives value A or B using password-derived scalar s
  3. Generates zero-knowledge proof for the combined exponent
  4. Exchanges computed values and proofs

Note: Round 2 is ordered deterministically to avoid race conditions. Alice sends A first; after receiving A, Bob sends B.

Key Derivation

  1. Both parties compute the shared secret K
  2. Derive encKey and macKey using HKDF(SHA-256) with salt = H(transcript) and info = "lockstep/v1:enc|mac" (512 bits total)
  3. Derive a dedicated key-confirmation key k' using HKDF(SHA-256) with info = "lockstep/v1:kc"
  4. Establish AES-256-GCM using encKey; AAD = contextBytes || messageCounter

Security Parameters

Parameter Value Notes
Prime Group 2048-bit safe prime RFC 3526 Group 14
Generator g = 2² mod p q-subgroup generator, validated
Password Hashing SHA-256 Immediate hashing with room ID as salt
Password-to-Scalar HKDF Maps to s ∈ [1, q-1], never 0 mod q
Key Derivation HKDF(SHA-256) salt = H(transcript)
Encryption AES-256-GCM 96-bit IV (4 random + 8 counter), 128-bit tag
ZKP Binding SHA-256 Binds p, q, g, value, V, context, transcript, label

Security Enhancements

This implementation includes several security enhancements beyond the base J-PAKE protocol:

  1. Immediate Password Hashing: Passwords are hashed with SHA-256 immediately upon entry, using the room ID as salt. The plaintext password is never stored in memory or passed to the J-PAKE protocol.

  2. HKDF-based Key Separation: From the shared secret K, derive encKey and macKey via HKDF(SHA-256) with salt = H(transcript) and info = "lockstep/v1:enc|mac"; derive a dedicated key-confirmation key k' with info = "lockstep/v1:kc".

  3. Secure Memory Management: All sensitive data (password hashes, session keys) are explicitly cleared from memory when no longer needed, reducing the risk of memory disclosure attacks.

  4. Transcript/Context Binding: ZKP challenges and HKDF salts bind the session context and a canonical protocol transcript to prevent mix-and-match or MITM attacks.

Security Properties

Property Description
Mutual Authentication Both parties prove knowledge of the shared password
Forward Secrecy Compromise of long-term secrets doesn't affect past sessions
Offline Dictionary Resistance Online attacks only; offline attacks prevented
No PKI Required No certificates or pre-shared keys needed
Memory Protection Passwords never stored in plaintext; sensitive data cleared
Replay Protection Counters and IV-reuse guards prevent message replays

Testing

The project includes comprehensive test suites validating cryptographic correctness, protocol security, and end-to-end integration.

Prerequisites

  • Node.js ≥18 (required for native WebCrypto and ESM support)

Quick Start

cd client
npm install
npm test           # Run all test suites

Running Specific Test Suites

npm run test:unit         # Unit tests only (~5 seconds)
npm run test:integration  # Integration tests only (~20 seconds)
npm run test:security     # Security tests only (~15 seconds)
npm run test:watch        # Watch mode (auto-rerun on changes)
npm run test:coverage     # Generate coverage report

Test Suite Overview

Suite File Purpose Runtime
Unit jpake.test.js Cryptographic primitives, RFC 8236 compliance ~5 seconds
Integration integration.test.js Full client-server protocol flows over Socket.IO ~20 seconds
Security security.test.js Attack resistance, input validation, authorization ~15 seconds

What's Tested

Unit Tests (jpake.test.js):

  • Group math (MODP Group 14, q-subgroup validation)
  • Password-to-scalar mapping (deterministic, context-bound)
  • Schnorr zero-knowledge proofs (generation and verification)
  • Full J-PAKE protocol flow (Round 1, Round 2, key derivation)
  • Key confirmation mechanism (explicit KC₁/KC₂ exchange)
  • AES-256-GCM AEAD channel (encryption, decryption, AAD binding)
  • Replay protection (IV uniqueness, counter tracking)
  • State machine integrity (sequential round enforcement)
  • RFC 8236 §2.2 compliance (UserID validation in ZKPs)

Integration Tests (integration.test.js):

  • Room management (join, leave, capacity limits)
  • Participant tracking and notifications
  • Protocol initiation (ready_for_protocol signaling)
  • Full J-PAKE exchange over WebSocket
  • Encrypted message delivery and ordering

Security Tests (security.test.js):

  • Input validation (injection prevention, size limits)
  • Authorization enforcement (room membership checks)
  • Cryptographic attack resistance (tampered proofs, subgroup attacks)
  • Context binding (cross-room/session attack prevention)
  • Replay and reorder protection
  • State machine enforcement

Test Directory Structure

client/tests/
├── setup.mjs              # WebCrypto polyfill for Node.js
├── helpers.js             # Shared test utilities
├── jpake.test.js          # Unit tests
├── integration.test.js    # Integration tests
├── security.test.js       # Security tests
└── helpers/
    ├── performanceCollector.js
    ├── statistics.js
    └── reporter.js

Debugging Tips

  • WebCrypto undefined: Ensure Node.js ≥18 and tests/setup.mjs is loaded
  • ESM import errors: Verify "type": "module" in package.json
  • Test timeouts: Increase timeout for network-bound tests
  • Port conflicts: Integration tests use port 3002, security tests use port 3003

Performance

Executive Summary

Performance testing was conducted using Playwright v1.56 to validate RFC 8236 compliance and measure cryptographic operation timing across multiple browser engines.

Key Findings:

  • Full J-PAKE handshake completes in 711ms on Chromium (sub-second)
  • All browsers successfully complete the protocol with consistent 28 modPow operations
  • Firefox 2.0x slower than Chromium (expected due to BigInt performance)
  • WebKit/Safari performance nearly identical to Chromium (~760ms, only 7% slower)
  • RFC 8236 validated: 28 modPow calls = 14 conceptual exponentiations × 2 (proof verification)

Handshake Latency Results

Browser Wall Clock Time modPow Ops vs. Chromium Status
Chromium 711 ms 28 baseline PASS
Firefox 1,399 ms 28 +97% slower PASS
WebKit 759 ms 28 +6.8% slower PASS
iOS (emulated) 764 ms 28 +7.5% slower PASS
Android (emulated) 711 ms 28 +0% (identical) PASS

Per-Phase Timing

Round 1 Generation:

  • Chromium: 69-70ms
  • WebKit: 77-80ms
  • Firefox: 184-192ms

Round 2 Generation:

  • Chromium: 46ms
  • WebKit: 51-52ms
  • Firefox: 124-127ms

RFC 8236 Compliance

The RFC estimates approximately 14 modular exponentiations per party. Our measured 28 modPow operations aligns with this estimate:

Metric RFC 8236 Estimate Measured Compliance
Exponentiations per party ~14 (conceptual) 28 (modPow calls) PASS
Protocol rounds 2 + KC 2 + KC PASS
Group parameters MODP 2048 MODP 2048 PASS
Proof type Schnorr NIZK Schnorr NIZK PASS

Note: The difference is measurement granularity—RFC counts abstract operations (e.g., "verify Schnorr proof" = 1), while we measure raw modPow() calls (e.g., "verify Schnorr proof" = g^r + D^c = 2).

Message Sizes

Phase Approximate Size Components
Round 1 ~1.5 KB 2 group elements + 2 Schnorr proofs
Round 2 ~0.8 KB 1 group element + 1 Schnorr proof
Key Confirmation ~64 bytes HMAC tag (256-bit)
Encrypted Message Content + 64 bytes AES-GCM: IV (12B) + tag (16B) + overhead

Total protocol overhead: ~2.4 KB per handshake

Running Performance Tests

# Start server and client first
cd server && npm start &
cd client && npm start &

# Run performance tests
cd client
npm run test:perf              # All browsers
npm run test:perf:chromium     # Chromium only
npm run test:perf:firefox      # Firefox only
npm run test:perf:webkit       # WebKit only
npm run test:perf:mobile       # Mobile emulation
npm run test:perf:headed       # Visual browser mode

Mobile Testing Caveats

Mobile tests use device emulation, not real hardware. Results may differ from actual devices due to:

  • CPU performance differences (desktop vs. mobile ARM)
  • Thermal throttling on real devices
  • JavaScript engine variations (especially iOS Safari)

Estimated Real Device Performance:

  • iOS Safari: 1.2-1.8 seconds
  • Android Chrome: 0.9-1.2 seconds

API Reference

WebSocket Events

Client → Server

Event Payload Description
join_room {roomId, username} Join a room
jpake_round1 Round 1 data J-PAKE round 1 exchange
jpake_round2 Round 2 data J-PAKE round 2 exchange
kc1 {tag} Key confirmation (Alice tag)
kc2 {tag} Key confirmation response (Bob tag)
secure_message {message, ctr} Encrypted message with counter

Server → Client

Event Payload Description
room_joined Room info Confirmation of room join
participant_joined User info New participant notification
participant_left User info Participant departure
ready_for_protocol {isInitiator, participantCount, participants, sessionId} Signal to start J-PAKE
jpake_round1 Round 1 data Relay round 1 from peer
jpake_round2 Round 2 data Relay round 2 from peer
kc1 {tag} Relay key confirmation (Alice tag)
kc2 {tag} Relay key confirmation (Bob tag)
secure_message {sender, message, ctr} Relay encrypted message

REST Endpoints

Method Endpoint Description
GET /api/status Server status and statistics
GET /api/rooms List active rooms (debugging)

Project Structure

lockstep-chat/
├── client/                         # React frontend
│   ├── public/
│   │   ├── index.html              # HTML template
│   │   └── favicon.ico             # Application icon
│   ├── src/
│   │   ├── components/             # React components
│   │   │   └── SecurityTooltip.js  # Security details modal
│   │   ├── utils/                  # Utility modules
│   │   │   ├── validation.js       # Input validation
│   │   │   ├── crypto.js           # Cryptographic utilities
│   │   │   └── time.js             # Time formatting
│   │   ├── App.js                  # Main application component
│   │   ├── App.css                 # Styles with theme support
│   │   ├── jpake.js                # J-PAKE protocol implementation
│   │   └── index.js                # React entry point
│   ├── tests/                      # Test suites
│   │   ├── jpake.test.js           # Unit tests
│   │   ├── integration.test.js     # Integration tests
│   │   ├── security.test.js        # Security tests
│   │   └── helpers/                # Test utilities
│   └── package.json                # Client dependencies
├── server/                         # Node.js backend
│   ├── server.js                   # WebSocket relay server
│   └── package.json                # Server dependencies
├── start.sh                        # Startup script (macOS/Linux)
├── start.bat                       # Startup script (Windows)
├── start.js                        # Startup script (Node.js)
├── README.md                       # This file
└── LICENSE                         # MIT License

Security Considerations

Production Deployment

  1. HTTPS Required: Always use TLS in production
  2. Password Requirements: Enforce strong password policies
  3. Rate Limiting: Implement rate limiting to prevent brute force
  4. Input Validation: Validate all inputs on both client and server
  5. Content Security Policy: Implement CSP headers

Known Limitations

Limitation Impact Mitigation
Password Quality Security depends on password entropy Recommend strong passwords
Online Attacks Vulnerable to online dictionary attacks Rate limiting needed
Side Channels JavaScript timing attacks possible Browser crypto trusted
Browser Security Relies on browser's crypto implementation Use modern browsers

Troubleshooting

Common Issues

WebCrypto undefined:

  • Ensure Node.js ≥18 is installed
  • Verify tests/setup.mjs is loaded in Jest config

"Cannot use import statement outside a module":

  • Ensure package.json has "type": "module"

Socket connection failures:

  • Verify server running on port 3001
  • Check CORS configuration

Test timeouts:

  • Increase timeout for network-bound tests
  • Ensure server is fully started

Port Conflicts

# Find and kill process on specific port
lsof -ti:3001 | xargs kill -9
lsof -ti:3002 | xargs kill -9

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch
  3. Write tests first (TDD approach)
  4. Commit your changes
  5. Push to the branch
  6. Open a Pull Request

Code Style

  • ES6+ JavaScript
  • Functional React components with hooks
  • Consistent error handling
  • Comprehensive comments for complex operations

Changelog

Version 2.1.0 - Code Organization and Documentation

  • Refactored: Extracted utility functions into dedicated modules
  • Refactored: Extracted SecurityTooltip component
  • Improved: Added comprehensive JSDoc documentation
  • Enhanced: Better separation of concerns

Version 2.0.0 - Protocol Hardening and Interop

  • Changed: HKDF(SHA-256)-based key separation for encKey/macKey
  • Changed: Password-to-scalar mapping s ∈ [1, q-1] via HKDF
  • Improved: Transcript-bound HKDF salt and canonical transcript ordering
  • Improved: Deterministic Round 2 ordering
  • Improved: AES-GCM AAD binds session context and message counter

Version 1.0.0 - Initial Release

  • Complete J-PAKE protocol implementation
  • AES-256-GCM encryption
  • WebSocket-based real-time communication
  • React-based UI with theme support

Citation

If you use this implementation in your research, teaching, or projects:

@software{vermillion2025lockstep,
  author       = {Vermillion, Justin},
  title        = {Lockstep Chat: A J-PAKE Secure Messaging Application},
  year         = {2025},
  institution  = {New Mexico Institute of Mining and Technology},
  course       = {CYBS 5041: Applied Cryptography},
  url          = {https://github.com/vermi/lockstep-chat}
}

References


License

MIT License - see LICENSE file for details


Acknowledgments

  • J-PAKE protocol designed by F. Hao and P. Ryan
  • RFC 8236 specification by F. Hao
  • React team for the excellent framework
  • Socket.IO team for real-time capabilities

Academic Disclaimer: This implementation was created as an educational project for CYBS 5041 at New Mexico Tech. While it demonstrates proper cryptographic principles and includes security enhancements, it is intended primarily for learning and demonstration purposes. For production use, consider additional security measures, professional cryptographic review, and compliance with relevant security standards.