IoT Data Pipeline Architecture: From Sensors to Analytics with MQTT and Apache Spark
Combining our expertise in MQTT messaging and Apache Spark processing, this comprehensive guide demonstrates building …
Building upon our embedded programming expertise from AVR programming and Arduino development, this guide explores critical security implementations for protecting embedded systems and IoT devices against modern threats.
1// embedded_crypto.h
2#ifndef EMBEDDED_CRYPTO_H
3#define EMBEDDED_CRYPTO_H
4
5#include <stdint.h>
6#include <string.h>
7#include <avr/pgmspace.h>
8
9// AES-128 implementation for AVR microcontrollers
10typedef struct {
11 uint8_t round_keys[176]; // 11 round keys * 16 bytes
12 uint8_t initialized;
13} aes128_context_t;
14
15// Lightweight cryptographic primitives
16void aes128_init(aes128_context_t* ctx, const uint8_t* key);
17void aes128_encrypt_block(aes128_context_t* ctx, const uint8_t* plaintext, uint8_t* ciphertext);
18void aes128_decrypt_block(aes128_context_t* ctx, const uint8_t* ciphertext, uint8_t* plaintext);
19
20// ChaCha20 stream cipher (more efficient for some embedded systems)
21typedef struct {
22 uint32_t state[16];
23 uint8_t buffer[64];
24 uint8_t buffer_pos;
25} chacha20_context_t;
26
27void chacha20_init(chacha20_context_t* ctx, const uint8_t* key, const uint8_t* nonce);
28void chacha20_encrypt(chacha20_context_t* ctx, const uint8_t* plaintext,
29 uint8_t* ciphertext, size_t length);
30
31// SHA-256 hash function
32typedef struct {
33 uint32_t state[8];
34 uint64_t count;
35 uint8_t buffer[64];
36} sha256_context_t;
37
38void sha256_init(sha256_context_t* ctx);
39void sha256_update(sha256_context_t* ctx, const uint8_t* data, size_t length);
40void sha256_final(sha256_context_t* ctx, uint8_t hash[32]);
41
42// HMAC-SHA256 for message authentication
43void hmac_sha256(const uint8_t* key, size_t key_len,
44 const uint8_t* message, size_t msg_len,
45 uint8_t hmac[32]);
46
47#endif // EMBEDDED_CRYPTO_H
48
49// embedded_crypto.c
50#include "embedded_crypto.h"
51
52// AES S-box stored in program memory to save RAM
53static const uint8_t aes_sbox[256] PROGMEM = {
54 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
55 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
56 // ... (full S-box table would be here)
57};
58
59// Round constants for AES key expansion
60static const uint8_t rcon[11] PROGMEM = {
61 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
62};
63
64void aes128_init(aes128_context_t* ctx, const uint8_t* key) {
65 uint8_t i, j;
66 uint8_t temp[4];
67
68 // Copy initial key
69 memcpy(ctx->round_keys, key, 16);
70
71 // Key expansion
72 for (i = 1; i < 11; i++) {
73 // Copy last 4 bytes of previous round key
74 memcpy(temp, &ctx->round_keys[(i-1)*16 + 12], 4);
75
76 // RotWord and SubBytes
77 uint8_t temp_byte = temp[0];
78 temp[0] = pgm_read_byte(&aes_sbox[temp[1]]);
79 temp[1] = pgm_read_byte(&aes_sbox[temp[2]]);
80 temp[2] = pgm_read_byte(&aes_sbox[temp[3]]);
81 temp[3] = pgm_read_byte(&aes_sbox[temp_byte]);
82
83 // XOR with Rcon
84 temp[0] ^= pgm_read_byte(&rcon[i]);
85
86 // Generate round key
87 for (j = 0; j < 16; j++) {
88 if (j < 4) {
89 ctx->round_keys[i*16 + j] = ctx->round_keys[(i-1)*16 + j] ^ temp[j];
90 } else {
91 ctx->round_keys[i*16 + j] = ctx->round_keys[(i-1)*16 + j] ^
92 ctx->round_keys[i*16 + j - 4];
93 }
94 }
95 }
96
97 ctx->initialized = 1;
98}
99
100// Secure random number generation for embedded systems
101typedef struct {
102 uint32_t state[4];
103 uint8_t initialized;
104} secure_prng_t;
105
106static secure_prng_t global_prng = {0};
107
108void secure_prng_init(const uint8_t* seed, size_t seed_len) {
109 sha256_context_t sha_ctx;
110 uint8_t hash[32];
111
112 sha256_init(&sha_ctx);
113 sha256_update(&sha_ctx, seed, seed_len);
114
115 // Add entropy from ADC noise
116 for (int i = 0; i < 16; i++) {
117 uint16_t noise = read_adc_noise(); // Implementation specific
118 sha256_update(&sha_ctx, (uint8_t*)&noise, 2);
119 }
120
121 sha256_final(&sha_ctx, hash);
122
123 // Initialize PRNG state
124 memcpy(global_prng.state, hash, 16);
125 global_prng.initialized = 1;
126}
127
128uint32_t secure_random_uint32() {
129 if (!global_prng.initialized) {
130 // Emergency initialization with available entropy
131 uint8_t emergency_seed[16];
132 for (int i = 0; i < 16; i++) {
133 emergency_seed[i] = read_adc_noise() & 0xFF;
134 }
135 secure_prng_init(emergency_seed, 16);
136 }
137
138 // Xorshift128 algorithm (fast and secure enough for embedded use)
139 uint32_t t = global_prng.state[3];
140 uint32_t s = global_prng.state[0];
141
142 global_prng.state[3] = global_prng.state[2];
143 global_prng.state[2] = global_prng.state[1];
144 global_prng.state[1] = s;
145
146 t ^= t << 11;
147 t ^= t >> 8;
148 global_prng.state[0] = t ^ s ^ (s >> 19);
149
150 return global_prng.state[0];
151}
152
153void secure_random_bytes(uint8_t* buffer, size_t length) {
154 for (size_t i = 0; i < length; i += 4) {
155 uint32_t random = secure_random_uint32();
156 size_t copy_len = (length - i < 4) ? (length - i) : 4;
157 memcpy(&buffer[i], &random, copy_len);
158 }
159}
1// secure_boot.h
2#ifndef SECURE_BOOT_H
3#define SECURE_BOOT_H
4
5#include <stdint.h>
6#include <stdbool.h>
7
8// Boot configuration
9#define BOOTLOADER_VERSION 0x0100
10#define APPLICATION_START_ADDRESS 0x4000
11#define SIGNATURE_SIZE 32
12#define PUBLIC_KEY_SIZE 32
13
14// Boot status codes
15typedef enum {
16 BOOT_SUCCESS = 0,
17 BOOT_ERROR_INVALID_SIGNATURE = 1,
18 BOOT_ERROR_CORRUPTED_IMAGE = 2,
19 BOOT_ERROR_VERSION_MISMATCH = 3,
20 BOOT_ERROR_ROLLBACK_PROTECTION = 4
21} boot_status_t;
22
23// Application header structure
24typedef struct __attribute__((packed)) {
25 uint32_t magic; // Magic number for validation
26 uint32_t version; // Application version
27 uint32_t size; // Application size in bytes
28 uint32_t crc32; // CRC32 checksum
29 uint8_t signature[SIGNATURE_SIZE]; // Digital signature
30 uint32_t timestamp; // Build timestamp
31 uint8_t reserved[12]; // Reserved for future use
32} app_header_t;
33
34// Secure boot functions
35boot_status_t secure_boot_verify_application(void);
36void secure_boot_jump_to_application(void);
37bool verify_digital_signature(const uint8_t* data, size_t data_len,
38 const uint8_t* signature);
39
40#endif // SECURE_BOOT_H
41
42// secure_boot.c
43#include "secure_boot.h"
44#include "embedded_crypto.h"
45#include <avr/io.h>
46#include <avr/boot.h>
47#include <avr/wdt.h>
48
49// Public key for signature verification (stored in bootloader)
50static const uint8_t public_key[PUBLIC_KEY_SIZE] PROGMEM = {
51 0x1a, 0x2b, 0x3c, 0x4d, 0x5e, 0x6f, 0x7a, 0x8b,
52 0x9c, 0xad, 0xbe, 0xcf, 0xda, 0xeb, 0xfc, 0x0d,
53 0x1e, 0x2f, 0x3a, 0x4b, 0x5c, 0x6d, 0x7e, 0x8f,
54 0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xfa, 0x0b
55};
56
57// Application magic number
58#define APP_MAGIC 0xDEADBEEF
59
60boot_status_t secure_boot_verify_application(void) {
61 app_header_t* header = (app_header_t*)APPLICATION_START_ADDRESS;
62
63 // Check magic number
64 if (header->magic != APP_MAGIC) {
65 return BOOT_ERROR_CORRUPTED_IMAGE;
66 }
67
68 // Verify CRC32
69 uint32_t calculated_crc = calculate_crc32(
70 (uint8_t*)(APPLICATION_START_ADDRESS + sizeof(app_header_t)),
71 header->size
72 );
73
74 if (calculated_crc != header->crc32) {
75 return BOOT_ERROR_CORRUPTED_IMAGE;
76 }
77
78 // Verify digital signature
79 bool signature_valid = verify_digital_signature(
80 (uint8_t*)(APPLICATION_START_ADDRESS + sizeof(app_header_t)),
81 header->size,
82 header->signature
83 );
84
85 if (!signature_valid) {
86 return BOOT_ERROR_INVALID_SIGNATURE;
87 }
88
89 // Check version for rollback protection
90 uint32_t stored_version = read_stored_version();
91 if (header->version < stored_version) {
92 return BOOT_ERROR_ROLLBACK_PROTECTION;
93 }
94
95 // Update stored version if newer
96 if (header->version > stored_version) {
97 store_version(header->version);
98 }
99
100 return BOOT_SUCCESS;
101}
102
103void secure_boot_jump_to_application(void) {
104 // Disable watchdog
105 wdt_disable();
106
107 // Disable interrupts
108 cli();
109
110 // Clear any pending interrupts
111 EIFR = 0xFF;
112 PCIFR = 0xFF;
113
114 // Jump to application
115 void (*app_start)(void) = (void (*)(void))APPLICATION_START_ADDRESS;
116 app_start();
117}
118
119bool verify_digital_signature(const uint8_t* data, size_t data_len,
120 const uint8_t* signature) {
121 // Simple Ed25519-like verification (simplified implementation)
122 // In practice, use a proper Ed25519 library
123
124 uint8_t hash[32];
125 sha256_context_t sha_ctx;
126
127 // Hash the data
128 sha256_init(&sha_ctx);
129 sha256_update(&sha_ctx, data, data_len);
130 sha256_final(&sha_ctx, hash);
131
132 // Verify signature (simplified - real implementation would be more complex)
133 uint8_t expected_signature[32];
134 hmac_sha256(public_key, PUBLIC_KEY_SIZE, hash, 32, expected_signature);
135
136 return memcmp(signature, expected_signature, 32) == 0;
137}
138
139// Bootloader main function
140int main(void) {
141 // Initialize hardware
142 init_hardware();
143
144 // Initialize secure PRNG
145 uint8_t entropy[16];
146 collect_hardware_entropy(entropy, 16);
147 secure_prng_init(entropy, 16);
148
149 // Check for forced bootloader mode
150 if (check_bootloader_pin()) {
151 enter_bootloader_mode();
152 return 0;
153 }
154
155 // Verify application
156 boot_status_t status = secure_boot_verify_application();
157
158 if (status == BOOT_SUCCESS) {
159 // Application is valid, jump to it
160 secure_boot_jump_to_application();
161 } else {
162 // Application verification failed
163 handle_boot_error(status);
164 enter_bootloader_mode();
165 }
166
167 return 0;
168}
1// secure_comm.h
2#ifndef SECURE_COMM_H
3#define SECURE_COMM_H
4
5#include <stdint.h>
6#include <stdbool.h>
7
8// Protocol definitions
9#define PROTOCOL_VERSION 0x01
10#define MAX_PAYLOAD_SIZE 128
11#define SESSION_KEY_SIZE 16
12#define NONCE_SIZE 12
13#define TAG_SIZE 16
14
15// Message types
16typedef enum {
17 MSG_HANDSHAKE_REQUEST = 0x01,
18 MSG_HANDSHAKE_RESPONSE = 0x02,
19 MSG_DATA = 0x03,
20 MSG_ACK = 0x04,
21 MSG_ERROR = 0xFF
22} message_type_t;
23
24// Secure message structure
25typedef struct __attribute__((packed)) {
26 uint8_t version;
27 uint8_t msg_type;
28 uint16_t sequence;
29 uint8_t nonce[NONCE_SIZE];
30 uint16_t payload_len;
31 uint8_t payload[MAX_PAYLOAD_SIZE];
32 uint8_t tag[TAG_SIZE]; // Authentication tag
33} secure_message_t;
34
35// Session context
36typedef struct {
37 uint8_t session_key[SESSION_KEY_SIZE];
38 uint32_t tx_sequence;
39 uint32_t rx_sequence;
40 bool authenticated;
41 uint32_t last_heartbeat;
42} session_context_t;
43
44// Function prototypes
45bool secure_comm_init(session_context_t* session);
46bool secure_comm_handshake(session_context_t* session);
47bool secure_comm_send_data(session_context_t* session,
48 const uint8_t* data, size_t length);
49bool secure_comm_receive_data(session_context_t* session,
50 uint8_t* data, size_t* length);
51void secure_comm_cleanup(session_context_t* session);
52
53#endif // SECURE_COMM_H
54
55// secure_comm.c
56#include "secure_comm.h"
57#include "embedded_crypto.h"
58#include <string.h>
59
60// Pre-shared key (in practice, would be provisioned securely)
61static const uint8_t psk[32] PROGMEM = {
62 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
63 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
64 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
65 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99
66};
67
68bool secure_comm_init(session_context_t* session) {
69 // Clear session context
70 memset(session, 0, sizeof(session_context_t));
71
72 // Initialize random session key
73 secure_random_bytes(session->session_key, SESSION_KEY_SIZE);
74
75 session->tx_sequence = 1;
76 session->rx_sequence = 0;
77 session->authenticated = false;
78
79 return true;
80}
81
82bool secure_comm_handshake(session_context_t* session) {
83 secure_message_t msg;
84 uint8_t challenge[16];
85 uint8_t response[16];
86
87 // Generate random challenge
88 secure_random_bytes(challenge, 16);
89
90 // Prepare handshake request
91 msg.version = PROTOCOL_VERSION;
92 msg.msg_type = MSG_HANDSHAKE_REQUEST;
93 msg.sequence = session->tx_sequence++;
94 secure_random_bytes(msg.nonce, NONCE_SIZE);
95
96 // Encrypt challenge with PSK
97 aes128_context_t aes_ctx;
98 aes128_init(&aes_ctx, psk);
99 aes128_encrypt_block(&aes_ctx, challenge, msg.payload);
100 msg.payload_len = 16;
101
102 // Calculate HMAC for authentication
103 uint8_t hmac_input[sizeof(msg) - TAG_SIZE];
104 memcpy(hmac_input, &msg, sizeof(msg) - TAG_SIZE);
105 hmac_sha256(psk, 32, hmac_input, sizeof(hmac_input), msg.tag);
106
107 // Send handshake request
108 if (!send_message(&msg)) {
109 return false;
110 }
111
112 // Wait for handshake response
113 if (!receive_message(&msg)) {
114 return false;
115 }
116
117 // Verify handshake response
118 if (msg.msg_type != MSG_HANDSHAKE_RESPONSE) {
119 return false;
120 }
121
122 // Verify HMAC
123 uint8_t calculated_tag[TAG_SIZE];
124 memcpy(hmac_input, &msg, sizeof(msg) - TAG_SIZE);
125 hmac_sha256(psk, 32, hmac_input, sizeof(hmac_input), calculated_tag);
126
127 if (memcmp(calculated_tag, msg.tag, TAG_SIZE) != 0) {
128 return false;
129 }
130
131 // Decrypt response
132 aes128_decrypt_block(&aes_ctx, msg.payload, response);
133
134 // Verify challenge response (should be challenge XOR with known pattern)
135 uint8_t expected_response[16];
136 for (int i = 0; i < 16; i++) {
137 expected_response[i] = challenge[i] ^ 0xAA;
138 }
139
140 if (memcmp(response, expected_response, 16) == 0) {
141 session->authenticated = true;
142 session->last_heartbeat = get_timestamp();
143 return true;
144 }
145
146 return false;
147}
148
149bool secure_comm_send_data(session_context_t* session,
150 const uint8_t* data, size_t length) {
151 if (!session->authenticated || length > MAX_PAYLOAD_SIZE) {
152 return false;
153 }
154
155 secure_message_t msg;
156 chacha20_context_t cipher_ctx;
157
158 // Prepare message header
159 msg.version = PROTOCOL_VERSION;
160 msg.msg_type = MSG_DATA;
161 msg.sequence = session->tx_sequence++;
162 secure_random_bytes(msg.nonce, NONCE_SIZE);
163 msg.payload_len = length;
164
165 // Encrypt payload
166 chacha20_init(&cipher_ctx, session->session_key, msg.nonce);
167 chacha20_encrypt(&cipher_ctx, data, msg.payload, length);
168
169 // Calculate authentication tag
170 uint8_t auth_data[sizeof(msg) - TAG_SIZE];
171 memcpy(auth_data, &msg, sizeof(msg) - TAG_SIZE);
172 hmac_sha256(session->session_key, SESSION_KEY_SIZE,
173 auth_data, sizeof(auth_data), msg.tag);
174
175 // Send message
176 return send_message(&msg);
177}
178
179bool secure_comm_receive_data(session_context_t* session,
180 uint8_t* data, size_t* length) {
181 if (!session->authenticated) {
182 return false;
183 }
184
185 secure_message_t msg;
186 chacha20_context_t cipher_ctx;
187
188 // Receive message
189 if (!receive_message(&msg)) {
190 return false;
191 }
192
193 // Verify message type and sequence
194 if (msg.msg_type != MSG_DATA || msg.sequence <= session->rx_sequence) {
195 return false;
196 }
197
198 // Verify authentication tag
199 uint8_t auth_data[sizeof(msg) - TAG_SIZE];
200 uint8_t calculated_tag[TAG_SIZE];
201
202 memcpy(auth_data, &msg, sizeof(msg) - TAG_SIZE);
203 hmac_sha256(session->session_key, SESSION_KEY_SIZE,
204 auth_data, sizeof(auth_data), calculated_tag);
205
206 if (memcmp(calculated_tag, msg.tag, TAG_SIZE) != 0) {
207 return false;
208 }
209
210 // Decrypt payload
211 chacha20_init(&cipher_ctx, session->session_key, msg.nonce);
212 chacha20_encrypt(&cipher_ctx, msg.payload, data, msg.payload_len);
213
214 *length = msg.payload_len;
215 session->rx_sequence = msg.sequence;
216 session->last_heartbeat = get_timestamp();
217
218 return true;
219}
1// hsm_interface.h
2#ifndef HSM_INTERFACE_H
3#define HSM_INTERFACE_H
4
5#include <stdint.h>
6#include <stdbool.h>
7
8// Hardware security features
9typedef enum {
10 HSM_FEATURE_RNG = 0x01,
11 HSM_FEATURE_AES = 0x02,
12 HSM_FEATURE_HASH = 0x04,
13 HSM_FEATURE_PKI = 0x08,
14 HSM_FEATURE_SECURE_STORAGE = 0x10
15} hsm_feature_t;
16
17// Key storage slots
18#define HSM_MAX_KEYS 16
19#define HSM_KEY_SIZE 32
20
21typedef enum {
22 HSM_KEY_TYPE_AES128 = 1,
23 HSM_KEY_TYPE_AES256 = 2,
24 HSM_KEY_TYPE_HMAC = 3,
25 HSM_KEY_TYPE_ECC_PRIVATE = 4,
26 HSM_KEY_TYPE_ECC_PUBLIC = 5
27} hsm_key_type_t;
28
29typedef struct {
30 uint8_t slot_id;
31 hsm_key_type_t key_type;
32 bool in_use;
33 bool permanent;
34 uint8_t access_policy;
35} hsm_key_info_t;
36
37// Function prototypes
38bool hsm_init(void);
39uint32_t hsm_get_features(void);
40bool hsm_generate_key(uint8_t slot_id, hsm_key_type_t key_type);
41bool hsm_import_key(uint8_t slot_id, hsm_key_type_t key_type, const uint8_t* key_data);
42bool hsm_encrypt_data(uint8_t key_slot, const uint8_t* plaintext,
43 uint8_t* ciphertext, size_t length);
44bool hsm_decrypt_data(uint8_t key_slot, const uint8_t* ciphertext,
45 uint8_t* plaintext, size_t length);
46bool hsm_sign_data(uint8_t key_slot, const uint8_t* data, size_t data_len,
47 uint8_t* signature);
48bool hsm_verify_signature(uint8_t key_slot, const uint8_t* data, size_t data_len,
49 const uint8_t* signature);
50
51#endif // HSM_INTERFACE_H
1// security_monitor.c
2#include "security_monitor.h"
3
4typedef struct {
5 uint32_t failed_auth_attempts;
6 uint32_t invalid_messages;
7 uint32_t timing_violations;
8 uint32_t memory_violations;
9 uint32_t last_attack_time;
10 bool lockdown_active;
11} security_state_t;
12
13static security_state_t security_state = {0};
14
15void security_monitor_init(void) {
16 memset(&security_state, 0, sizeof(security_state_t));
17
18 // Initialize watchdog for system integrity
19 wdt_enable(WDTO_2S);
20
21 // Set up memory protection if available
22 setup_memory_protection();
23
24 // Enable stack canary
25 enable_stack_protection();
26}
27
28void security_monitor_update(void) {
29 uint32_t current_time = get_timestamp();
30
31 // Reset counters periodically
32 if (current_time - security_state.last_attack_time > SECURITY_RESET_INTERVAL) {
33 security_state.failed_auth_attempts = 0;
34 security_state.invalid_messages = 0;
35 security_state.timing_violations = 0;
36 }
37
38 // Check for attack patterns
39 if (security_state.failed_auth_attempts > MAX_AUTH_FAILURES ||
40 security_state.invalid_messages > MAX_INVALID_MESSAGES ||
41 security_state.timing_violations > MAX_TIMING_VIOLATIONS) {
42
43 trigger_security_lockdown();
44 }
45
46 // Check system integrity
47 if (!verify_system_integrity()) {
48 trigger_security_alert(ALERT_SYSTEM_COMPROMISE);
49 }
50
51 // Feed watchdog if everything is OK
52 if (!security_state.lockdown_active) {
53 wdt_reset();
54 }
55}
56
57void trigger_security_lockdown(void) {
58 security_state.lockdown_active = true;
59 security_state.last_attack_time = get_timestamp();
60
61 // Disable non-essential peripherals
62 disable_non_essential_peripherals();
63
64 // Clear sensitive data from RAM
65 clear_sensitive_data();
66
67 // Log security event
68 log_security_event(SECURITY_EVENT_LOCKDOWN);
69
70 // Enter minimal functionality mode
71 enter_safe_mode();
72}
This comprehensive embedded systems security framework provides multiple layers of protection essential for modern IoT devices. The implementations cover cryptographic primitives, secure boot processes, encrypted communications, and intrusion detection suitable for resource-constrained environments.
For foundational embedded programming concepts, explore our AVR programming tutorials and advanced Arduino development guide.