%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22'}}}%%
flowchart LR
subgraph Node_A["Node A (Sender)"]
A1[Sensor Data] --> A2[Encrypt with Key]
A2 --> A3[Add Timestamp]
A3 --> A4[Calculate Hash]
A4 --> A5[Secure Packet]
end
A5 -->|Network| B1
subgraph Node_B["Node B (Receiver)"]
B1[Receive Packet] --> B2[Verify Hash]
B2 --> B3[Check Timestamp]
B3 --> B4[Decrypt with Key]
B4 --> B5[Valid Data]
end
style A1 fill:#16A085,color:#fff
style A5 fill:#E67E22,color:#fff
style B1 fill:#E67E22,color:#fff
style B5 fill:#16A085,color:#fff
1435 Encryption Labs and Practice Exercises
1435.1 Learning Objectives
By completing these hands-on labs, you will be able to:
- Implement symmetric encryption using XOR and observe encryption/decryption cycles
- Demonstrate classical ciphers with Caesar cipher and understand its weaknesses
- Create hash functions for data integrity verification
- Build Message Authentication Codes (MAC) to verify message authenticity
- Simulate key exchange using Diffie-Hellman concepts
- Visualize encryption operations through LED indicators and serial output
These labs let you build cryptographic systems on real hardware (simulated ESP32). You’ll see encryption in action - watch data transform into ciphertext, verify integrity with hashes, and experience what happens when attacks occur.
Important: The algorithms in these labs are simplified for educational purposes. Production IoT systems use libraries like mbedTLS with AES-GCM.
1435.2 Lab 1: Basic Cryptographic Operations
1435.2.1 Components
| Component | Purpose | Wokwi Element |
|---|---|---|
| ESP32 DevKit | Main controller | esp32:devkit-v1 |
| Green LED | Success indicator | led:green |
| Red LED | Failure indicator | led:red |
| Yellow LED | Processing indicator | led:yellow |
| Blue LED | Key exchange status | led:blue |
| Push Button 1 | Cycle through modes | button |
| Push Button 2 | Execute operation | button |
| Push Button 3 | Key exchange / attack mode | button |
1435.2.2 Circuit Connections
ESP32 Pin Connections:
----------------------
GPIO 2 --> Green LED (+) --> 220 ohm Resistor --> GND
GPIO 4 --> Red LED (+) --> 220 ohm Resistor --> GND
GPIO 5 --> Yellow LED (+) --> 220 ohm Resistor --> GND
GPIO 18 --> Blue LED (+) --> 220 ohm Resistor --> GND
GPIO 15 --> Button 1 --> GND (Mode Select)
GPIO 16 --> Button 2 --> GND (Execute)
GPIO 17 --> Button 3 --> GND (Key Exchange)
1435.2.3 Interactive Wokwi Simulator
- Wire first: Connect all components before pasting code
- Paste code: Copy the complete code into the editor
- Run: Click the green “Play” button
- Serial Monitor: View output in the Serial Monitor panel
- Buttons: Click on-screen buttons to interact
1435.2.4 Lab Code
// =====================================================
// Interactive Cryptographic Operations Lab - ESP32
// Demonstrates: XOR, Caesar, Hash, MAC, Key Exchange
// Educational purposes - NOT for production use
// =====================================================
// Pin Definitions
#define LED_SUCCESS 2
#define LED_FAILURE 4
#define LED_PROCESSING 5
#define LED_KEYEXCHANGE 18
#define BTN_MODE 15
#define BTN_EXECUTE 16
#define BTN_KEYEXCHANGE 17
// Crypto Mode Enumeration
enum CryptoMode {
MODE_XOR = 0,
MODE_CAESAR = 1,
MODE_HASH = 2,
MODE_MAC = 3,
MODE_KEYEXCHANGE = 4,
MODE_COUNT = 5
};
// Global Variables
CryptoMode currentMode = MODE_XOR;
bool attackMode = false;
String sharedKey = "SECRET";
int caesarShift = 3;
unsigned long lastDebounce = 0;
const unsigned long debounceDelay = 200;
// Diffie-Hellman parameters (educational only)
const int DH_PRIME = 23;
const int DH_BASE = 5;
int privateKeyA = 6;
int privateKeyB = 15;
// Sample messages
String messages[] = {
"SENSOR:TEMP=25C",
"ALERT:MOTION_DETECTED",
"CMD:UNLOCK_DOOR",
"DATA:HUMIDITY=60%",
"STATUS:ONLINE"
};
int messageIndex = 0;
void setup() {
Serial.begin(115200);
pinMode(LED_SUCCESS, OUTPUT);
pinMode(LED_FAILURE, OUTPUT);
pinMode(LED_PROCESSING, OUTPUT);
pinMode(LED_KEYEXCHANGE, OUTPUT);
pinMode(BTN_MODE, INPUT_PULLUP);
pinMode(BTN_EXECUTE, INPUT_PULLUP);
pinMode(BTN_KEYEXCHANGE, INPUT_PULLUP);
ledStartupSequence();
Serial.println("\n================================================");
Serial.println(" Interactive Cryptographic Operations Lab");
Serial.println("================================================");
Serial.println("\nControls:");
Serial.println(" Button 1 (GPIO 15): Cycle crypto mode");
Serial.println(" Button 2 (GPIO 16): Execute operation");
Serial.println(" Button 3 (GPIO 17): Key exchange / Toggle attack");
Serial.println("================================================\n");
printCurrentMode();
}
void loop() {
if (digitalRead(BTN_MODE) == LOW && millis() - lastDebounce > debounceDelay) {
lastDebounce = millis();
cycleMode();
}
if (digitalRead(BTN_EXECUTE) == LOW && millis() - lastDebounce > debounceDelay) {
lastDebounce = millis();
executeCurrentOperation();
}
if (digitalRead(BTN_KEYEXCHANGE) == LOW && millis() - lastDebounce > debounceDelay) {
lastDebounce = millis();
if (currentMode == MODE_KEYEXCHANGE) {
performKeyExchange();
} else {
toggleAttackMode();
}
}
}
// LED Control
void ledStartupSequence() {
int leds[] = {LED_SUCCESS, LED_FAILURE, LED_PROCESSING, LED_KEYEXCHANGE};
for (int i = 0; i < 4; i++) {
digitalWrite(leds[i], HIGH);
delay(150);
digitalWrite(leds[i], LOW);
}
}
void showProcessing() {
digitalWrite(LED_PROCESSING, HIGH);
digitalWrite(LED_SUCCESS, LOW);
digitalWrite(LED_FAILURE, LOW);
}
void showSuccess() {
digitalWrite(LED_PROCESSING, LOW);
digitalWrite(LED_SUCCESS, HIGH);
delay(500);
digitalWrite(LED_SUCCESS, LOW);
}
void showFailure() {
digitalWrite(LED_PROCESSING, LOW);
digitalWrite(LED_FAILURE, HIGH);
delay(500);
digitalWrite(LED_FAILURE, LOW);
}
// Mode Management
void cycleMode() {
currentMode = (CryptoMode)((currentMode + 1) % MODE_COUNT);
messageIndex = (messageIndex + 1) % 5;
printCurrentMode();
}
void printCurrentMode() {
Serial.println("\n----------------------------------------");
Serial.print("Current Mode: ");
switch (currentMode) {
case MODE_XOR:
Serial.println("XOR ENCRYPTION");
break;
case MODE_CAESAR:
Serial.println("CAESAR CIPHER");
break;
case MODE_HASH:
Serial.println("HASH FUNCTION");
break;
case MODE_MAC:
Serial.println("MESSAGE AUTHENTICATION CODE");
break;
case MODE_KEYEXCHANGE:
Serial.println("KEY EXCHANGE");
break;
}
Serial.print("Sample message: ");
Serial.println(messages[messageIndex]);
Serial.println("----------------------------------------");
}
void toggleAttackMode() {
attackMode = !attackMode;
Serial.print("\nAttack mode: ");
Serial.println(attackMode ? "ENABLED" : "DISABLED");
}
// Execute Operations
void executeCurrentOperation() {
showProcessing();
switch (currentMode) {
case MODE_XOR:
demonstrateXOR();
break;
case MODE_CAESAR:
demonstrateCaesar();
break;
case MODE_HASH:
demonstrateHash();
break;
case MODE_MAC:
demonstrateMAC();
break;
case MODE_KEYEXCHANGE:
Serial.println("Press Button 3 to perform key exchange");
showFailure();
break;
}
}
// XOR Encryption
void demonstrateXOR() {
Serial.println("\n========== XOR ENCRYPTION ==========");
String plaintext = messages[messageIndex];
String key = attackMode ? "WRONG" : sharedKey;
Serial.print("Plaintext: ");
Serial.println(plaintext);
Serial.print("Key: ");
Serial.println(key);
String ciphertext = xorEncrypt(plaintext, sharedKey);
Serial.print("Ciphertext: ");
printHex(ciphertext);
String decrypted = xorEncrypt(ciphertext, key);
Serial.print("Decrypted: ");
Serial.println(decrypted);
if (decrypted == plaintext) {
Serial.println("\n[SUCCESS] Decryption verified!");
showSuccess();
} else {
Serial.println("\n[FAILURE] Wrong key!");
showFailure();
}
}
String xorEncrypt(String text, String key) {
String result = "";
for (int i = 0; i < text.length(); i++) {
result += (char)(text[i] ^ key[i % key.length()]);
}
return result;
}
// Caesar Cipher
void demonstrateCaesar() {
Serial.println("\n========== CAESAR CIPHER ==========");
String plaintext = messages[messageIndex];
int shift = attackMode ? (caesarShift + 5) % 26 : caesarShift;
Serial.print("Plaintext: ");
Serial.println(plaintext);
Serial.print("Shift: ");
Serial.println(shift);
String encrypted = caesarEncrypt(plaintext, caesarShift);
Serial.print("Encrypted: ");
Serial.println(encrypted);
String decrypted = caesarDecrypt(encrypted, shift);
Serial.print("Decrypted: ");
Serial.println(decrypted);
if (decrypted == plaintext) {
Serial.println("\n[SUCCESS] Caesar cipher verified!");
showSuccess();
} else {
Serial.println("\n[FAILURE] Wrong shift!");
showFailure();
}
}
String caesarEncrypt(String text, int shift) {
String result = "";
for (int i = 0; i < text.length(); i++) {
char c = text[i];
if (c >= 'A' && c <= 'Z') {
c = ((c - 'A' + shift) % 26) + 'A';
}
result += c;
}
return result;
}
String caesarDecrypt(String text, int shift) {
return caesarEncrypt(text, 26 - (shift % 26));
}
// Hash Function
void demonstrateHash() {
Serial.println("\n========== HASH FUNCTION ==========");
String data = messages[messageIndex];
String tamperedData = attackMode ? (data + "X") : data;
Serial.print("Original: ");
Serial.println(data);
unsigned long originalHash = simpleHash(data);
Serial.print("Hash: 0x");
Serial.println(originalHash, HEX);
if (attackMode) {
Serial.print("Tampered: ");
Serial.println(tamperedData);
}
unsigned long receivedHash = simpleHash(tamperedData);
if (originalHash == receivedHash) {
Serial.println("\n[VERIFIED] Integrity confirmed!");
showSuccess();
} else {
Serial.println("\n[TAMPERED] Data modified!");
showFailure();
}
}
unsigned long simpleHash(String text) {
unsigned long hash = 5381;
for (int i = 0; i < text.length(); i++) {
hash = ((hash << 5) + hash) + text[i];
}
return hash;
}
// MAC
void demonstrateMAC() {
Serial.println("\n========== MAC ==========");
String data = messages[messageIndex];
String key = attackMode ? "FAKE_KEY" : sharedKey;
Serial.print("Data: ");
Serial.println(data);
unsigned long originalMAC = simpleHash(data + sharedKey);
Serial.print("Original MAC: 0x");
Serial.println(originalMAC, HEX);
unsigned long receivedMAC = simpleHash(data + key);
Serial.print("Received MAC: 0x");
Serial.println(receivedMAC, HEX);
if (originalMAC == receivedMAC) {
Serial.println("\n[AUTHENTICATED] MAC verified!");
showSuccess();
} else {
Serial.println("\n[FORGED] Wrong key or tampered!");
showFailure();
}
}
// Key Exchange
void performKeyExchange() {
Serial.println("\n========== DIFFIE-HELLMAN ==========");
digitalWrite(LED_KEYEXCHANGE, HIGH);
int publicA = modPow(DH_BASE, privateKeyA, DH_PRIME);
int publicB = modPow(DH_BASE, privateKeyB, DH_PRIME);
Serial.print("Alice public: ");
Serial.println(publicA);
Serial.print("Bob public: ");
Serial.println(publicB);
int secretA = modPow(publicB, privateKeyA, DH_PRIME);
int secretB = modPow(publicA, privateKeyB, DH_PRIME);
Serial.print("Alice secret: ");
Serial.println(secretA);
Serial.print("Bob secret: ");
Serial.println(secretB);
if (secretA == secretB) {
Serial.println("\n[SUCCESS] Shared secret established!");
showSuccess();
} else {
showFailure();
}
digitalWrite(LED_KEYEXCHANGE, LOW);
}
int modPow(int base, int exp, int mod) {
int result = 1;
base = base % mod;
while (exp > 0) {
if (exp % 2 == 1) {
result = (result * base) % mod;
}
exp = exp >> 1;
base = (base * base) % mod;
}
return result;
}
void printHex(String text) {
for (int i = 0; i < text.length(); i++) {
if ((byte)text[i] < 16) Serial.print("0");
Serial.print((byte)text[i], HEX);
Serial.print(" ");
}
Serial.println();
}1435.2.5 Lab Activities
Activity 1: XOR Encryption
- Run the simulation and observe the XOR mode
- Press Button 2 to encrypt a message
- Note how the same XOR operation encrypts and decrypts
- Press Button 3 to enable “attack mode” (wrong key)
- Observe how decryption fails with the wrong key
Activity 2: Hash Integrity
- Switch to HASH mode (Button 1)
- Execute to see the hash of the original message
- Enable attack mode to simulate tampering
- Observe how any change produces a completely different hash
Activity 3: Key Exchange
- Switch to KEY EXCHANGE mode
- Press Button 3 to perform Diffie-Hellman
- Observe that Alice and Bob compute the same shared secret
- Note that this works over an “insecure” channel
1435.3 Lab 2: Secure Message Exchange
This lab demonstrates a complete secure communication protocol with encryption, integrity, and timestamps.
1435.3.1 Learning Goals
- Combine multiple cryptographic primitives
- Implement replay attack prevention
- Understand authenticated encryption concepts
// Secure Message Exchange - ESP32
// Demonstrates: Encryption + Integrity + Replay Prevention
struct SecurePacket {
String senderId;
unsigned long timestamp;
String encryptedPayload;
unsigned long integrityHash;
};
const String SHARED_KEY = "IOT_SECRET_KEY";
void setup() {
Serial.begin(115200);
Serial.println("Secure Communication Demo");
// Scenario 1: Normal communication
Serial.println("\n=== SCENARIO 1: Normal Communication ===");
SecurePacket packet = createSecurePacket("SENSOR_A", "temp=25.5C", SHARED_KEY);
Serial.println("Sender creates secure packet:");
Serial.print(" Sender ID: ");
Serial.println(packet.senderId);
Serial.print(" Timestamp: ");
Serial.println(packet.timestamp);
Serial.print(" Encrypted: ");
printHex(packet.encryptedPayload);
Serial.print(" Hash: 0x");
Serial.println(packet.integrityHash, HEX);
// Receiver verifies
bool verified = verifySecurePacket(packet, SHARED_KEY);
if (verified) {
String decrypted = xorEncrypt(packet.encryptedPayload, SHARED_KEY);
Serial.print("\n[VERIFIED] Decrypted: ");
Serial.println(decrypted);
}
// Scenario 2: Tampering detection
Serial.println("\n=== SCENARIO 2: Tampering Detection ===");
SecurePacket tamperedPacket = packet;
tamperedPacket.encryptedPayload[0] ^= 0xFF; // Flip bits
verified = verifySecurePacket(tamperedPacket, SHARED_KEY);
if (!verified) {
Serial.println("[ALERT] Integrity check FAILED!");
Serial.println("[ACTION] Packet rejected");
}
// Scenario 3: Replay attack prevention
Serial.println("\n=== SCENARIO 3: Replay Prevention ===");
unsigned long currentTime = packet.timestamp + 10000; // 10 seconds later
unsigned long maxAge = 5000; // 5 second window
if (currentTime - packet.timestamp > maxAge) {
Serial.println("[ALERT] Packet too old - REPLAY ATTACK!");
Serial.println("[ACTION] Packet rejected");
}
}
void loop() {}
SecurePacket createSecurePacket(String sender, String payload, String key) {
SecurePacket pkt;
pkt.senderId = sender;
pkt.timestamp = millis();
pkt.encryptedPayload = xorEncrypt(payload, key);
String toHash = sender + String(pkt.timestamp) + pkt.encryptedPayload + key;
pkt.integrityHash = simpleHash(toHash);
return pkt;
}
bool verifySecurePacket(SecurePacket pkt, String key) {
String toHash = pkt.senderId + String(pkt.timestamp) + pkt.encryptedPayload + key;
return (simpleHash(toHash) == pkt.integrityHash);
}
String xorEncrypt(String text, String key) {
String result = "";
for (int i = 0; i < text.length(); i++) {
result += (char)(text[i] ^ key[i % key.length()]);
}
return result;
}
unsigned long simpleHash(String text) {
unsigned long hash = 5381;
for (int i = 0; i < text.length(); i++) {
hash = ((hash << 5) + hash) + text[i];
}
return hash;
}
void printHex(String text) {
for (int i = 0; i < text.length(); i++) {
if ((byte)text[i] < 16) Serial.print("0");
Serial.print((byte)text[i], HEX);
Serial.print(" ");
}
Serial.println();
}1435.3.2 Security Layers Demonstrated
1435.4 Challenge Exercises
The Vigenere cipher improves on Caesar by using a keyword to create multiple shift values.
Task: Modify the Caesar cipher to use a multi-character key like “IOTKEY”.
Hint: For each position i, use key[i % keyLength] to determine the shift amount.
Timestamps alone may not prevent replay attacks if clocks are not synchronized.
Task: Add a random nonce (number used once) that both parties track to reject duplicate messages.
Steps:
- Generate a random nonce for each message
- Include nonce in the packet structure
- Receiver maintains a list of recently seen nonces
- Reject any message with a previously seen nonce
Using the same key forever is dangerous. Implement key rotation.
Task: Create a system where after every 10 messages, derive a new session key:
newKey = hash(oldKey + counter)Both sender and receiver independently calculate the same new key.
This lab uses simplified algorithms for learning. Real IoT security requires:
| Lab Demo | Production System |
|---|---|
| XOR encryption | AES-128-GCM or AES-256-GCM |
| Caesar cipher | Not used (educational only) |
| DJB2 hash | SHA-256 or SHA-3 |
| Simple hash auth | HMAC-SHA256 |
| Hardcoded key | TLS key exchange + certificates |
Never deploy lab code in production. Use libraries like:
- ESP32: mbedTLS (built into ESP-IDF)
- Arduino: Crypto library or AES library
- DTLS: tinydtls for constrained devices
1435.5 Summary
These hands-on labs demonstrate:
- XOR encryption as the foundation of symmetric ciphers
- Caesar cipher and why classical ciphers are insecure
- Hash functions for data integrity verification
- MACs for authentication plus integrity
- Diffie-Hellman for secure key exchange
- Complete protocols combining encryption, integrity, and replay prevention
1435.6 What’s Next
Continue to Cipher Challenge Game to test your cryptography knowledge through interactive puzzles and progressively challenging cipher challenges.