Crypto API¶
The Crypto API provides cryptographic functions including SHA-256 hashing, HMAC, and secure random number generation.
Overview¶
The crypto module provides:
- SHA-256 hashing (single-shot and incremental)
- HMAC-SHA256
- Secure random number generation
- Hex encoding/decoding utilities
- Constant-time comparison
- Secure memory clearing
Header¶
Constants¶
#define EMBSEC_SHA256_SIZE 32 // SHA-256 hash size in bytes
#define EMBSEC_HMAC_SIZE 32 // HMAC-SHA256 size in bytes
SHA-256 Hashing¶
Single-Shot Hashing¶
embsec_sha256¶
Calculate SHA-256 hash of data in a single operation.
Parameters:
data: Input data to hashlen: Length of input datahash: Output buffer for hash (must be 32 bytes)
Example:
const char *message = "Hello, EmbSec!";
uint8_t hash[EMBSEC_SHA256_SIZE];
embsec_sha256((const uint8_t *)message, strlen(message), hash);
// Convert to hex for display
char hex[65];
embsec_bytes_to_hex(hash, EMBSEC_SHA256_SIZE, hex);
embsec_printf("SHA-256: %s\n", hex);
Incremental Hashing¶
For large data or streaming operations, use the incremental API:
embsec_sha256_init¶
Initialize SHA-256 context for incremental hashing.
embsec_sha256_update¶
Add data to the hash computation.
embsec_sha256_final¶
Finalize the hash computation and get result.
Example:
embsec_sha256_ctx_t ctx;
uint8_t hash[EMBSEC_SHA256_SIZE];
// Initialize
embsec_sha256_init(&ctx);
// Add data in chunks
embsec_sha256_update(&ctx, (const uint8_t *)"Hello, ", 7);
embsec_sha256_update(&ctx, (const uint8_t *)"EmbSec!", 7);
// Get final hash
embsec_sha256_final(&ctx, hash);
HMAC-SHA256¶
embsec_hmac_sha256¶
void embsec_hmac_sha256(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t data_len,
uint8_t hmac[32]);
Calculate HMAC-SHA256 of data using the provided key.
Parameters:
key: Secret keykey_len: Length of keydata: Input datadata_len: Length of input datahmac: Output buffer for HMAC (must be 32 bytes)
Example:
const char *key = "secret-key";
const char *message = "authenticated message";
uint8_t hmac[EMBSEC_HMAC_SIZE];
embsec_hmac_sha256((const uint8_t *)key, strlen(key),
(const uint8_t *)message, strlen(message),
hmac);
char hex[65];
embsec_bytes_to_hex(hmac, EMBSEC_HMAC_SIZE, hex);
embsec_printf("HMAC: %s\n", hex);
Random Number Generation¶
embsec_random_bytes¶
Generate cryptographically secure random bytes.
Parameters:
buffer: Buffer to fill with random datalen: Number of bytes to generate
Notes:
- Uses hardware RNG when available
- Falls back to software PRNG seeded from system state
- Suitable for cryptographic use
Example:
// Generate random key
uint8_t key[32];
embsec_random_bytes(key, sizeof(key));
// Generate random nonce
uint8_t nonce[16];
embsec_random_bytes(nonce, sizeof(nonce));
Utility Functions¶
embsec_bytes_to_hex¶
Convert binary data to lowercase hexadecimal string.
Parameters:
bytes: Input binary datalen: Number of bytes to converthex: Output buffer (must be at least2*len+1bytes)
Example:
uint8_t data[] = {0xDE, 0xAD, 0xBE, 0xEF};
char hex[9]; // 4 bytes * 2 + 1 for null terminator
embsec_bytes_to_hex(data, 4, hex);
embsec_printf("Hex: %s\n", hex); // Output: "deadbeef"
embsec_hex_to_bytes¶
Convert hexadecimal string to binary data.
Parameters:
hex: Input hex string (must be even length)hex_len: Length of hex stringbytes: Output buffer
Returns:
- Number of bytes written on success
- -1 on error (invalid hex or odd length)
Example:
const char *hex = "deadbeef";
uint8_t bytes[4];
int len = embsec_hex_to_bytes(hex, strlen(hex), bytes);
if (len > 0) {
embsec_printf("Converted %d bytes\n", len);
}
embsec_crypto_memcmp¶
Constant-time memory comparison to prevent timing attacks.
Parameters:
a: First bufferb: Second bufferlen: Number of bytes to compare
Returns:
- 0 if buffers are equal
- Non-zero if different
Important: Unlike memcmp(), this function always compares all bytes regardless of differences found.
Example:
uint8_t expected_hmac[32];
uint8_t received_hmac[32];
// ... compute HMACs ...
if (embsec_crypto_memcmp(expected_hmac, received_hmac, 32) == 0) {
embsec_printf("Authentication successful\n");
} else {
embsec_printf("Authentication failed\n");
}
embsec_secure_zero¶
Securely clear sensitive data from memory. This function is guaranteed not to be optimized out by the compiler.
Parameters:
ptr: Memory to clearlen: Number of bytes to clear
Example:
uint8_t secret_key[32];
// Use secret key...
// Clear it when done
embsec_secure_zero(secret_key, sizeof(secret_key));
Security Patterns¶
Password Hashing¶
void hash_password(const char *password, const uint8_t *salt,
size_t salt_len, uint8_t hash[32]) {
// Simple password hashing (for demonstration)
// In production, use a proper password hashing function
embsec_sha256_ctx_t ctx;
embsec_sha256_init(&ctx);
// Add salt
embsec_sha256_update(&ctx, salt, salt_len);
// Add password
embsec_sha256_update(&ctx, (const uint8_t *)password, strlen(password));
// Get hash
embsec_sha256_final(&ctx, hash);
// Clear sensitive data
embsec_secure_zero(password, strlen(password));
}
Message Authentication¶
typedef struct {
uint8_t data[256];
size_t len;
uint8_t mac[32];
} authenticated_message_t;
bool send_authenticated(const uint8_t *data, size_t len,
const uint8_t *key, size_t key_len,
authenticated_message_t *msg) {
if (len > sizeof(msg->data)) {
return false;
}
// Copy data
memcpy(msg->data, data, len);
msg->len = len;
// Calculate MAC
embsec_hmac_sha256(key, key_len, data, len, msg->mac);
return true;
}
bool verify_authenticated(const authenticated_message_t *msg,
const uint8_t *key, size_t key_len) {
uint8_t expected_mac[32];
// Calculate expected MAC
embsec_hmac_sha256(key, key_len, msg->data, msg->len, expected_mac);
// Constant-time comparison
return embsec_crypto_memcmp(msg->mac, expected_mac, 32) == 0;
}
Key Derivation¶
void derive_key(const uint8_t *master_key, size_t master_len,
const char *context, uint8_t derived[32]) {
embsec_sha256_ctx_t ctx;
embsec_sha256_init(&ctx);
embsec_sha256_update(&ctx, master_key, master_len);
embsec_sha256_update(&ctx, (const uint8_t *)context, strlen(context));
embsec_sha256_final(&ctx, derived);
}
// Usage
uint8_t master[32];
uint8_t enc_key[32], mac_key[32];
embsec_random_bytes(master, sizeof(master));
derive_key(master, sizeof(master), "encryption", enc_key);
derive_key(master, sizeof(master), "mac", mac_key);
Challenge-Response¶
bool challenge_response(void) {
uint8_t challenge[16];
uint8_t response[32];
uint8_t expected[32];
const uint8_t secret[32] = { /* pre-shared secret */ };
// Generate random challenge
embsec_random_bytes(challenge, sizeof(challenge));
// Send challenge
embsec_printf("Challenge: ");
for (int i = 0; i < 16; i++) {
embsec_printf("%02x", challenge[i]);
}
embsec_printf("\n");
// Calculate expected response
embsec_hmac_sha256(secret, sizeof(secret),
challenge, sizeof(challenge),
expected);
// Get response from user
char hex_response[65];
embsec_printf("Response: ");
embsec_gets(hex_response, sizeof(hex_response));
// Convert and verify
if (embsec_hex_to_bytes(hex_response, 64, response) == 32) {
if (embsec_crypto_memcmp(response, expected, 32) == 0) {
embsec_printf("Authentication successful!\n");
return true;
}
}
embsec_printf("Authentication failed!\n");
return false;
}
Performance Considerations¶
Timing¶
Typical performance on 80MHz Cortex-M4:
- SHA-256 (1KB): ~2-3ms
- HMAC-SHA256 (1KB): ~4-5ms
- Random bytes (32B): <1ms
Memory Usage¶
- SHA-256 context: ~112 bytes
- Stack usage: <512 bytes for all functions
- No dynamic allocation
Optimization Tips¶
-
Reuse contexts for multiple operations:
-
Process data in chunks:
-
Cache computed values:
Security Best Practices¶
1. Always Clear Sensitive Data¶
void process_secret(const uint8_t *secret, size_t len) {
uint8_t temp[64];
// Use secret...
memcpy(temp, secret, len);
// Always clear when done
embsec_secure_zero(temp, sizeof(temp));
}
2. Use Constant-Time Comparison¶
// BAD - timing attack vulnerable
if (memcmp(computed_mac, expected_mac, 32) == 0) { }
// GOOD - constant time
if (embsec_crypto_memcmp(computed_mac, expected_mac, 32) == 0) { }
3. Validate All Inputs¶
bool hash_user_input(const char *input, uint8_t hash[32]) {
// Validate input length
size_t len = strlen(input);
if (len == 0 || len > MAX_INPUT_LEN) {
return false;
}
// Validate characters
for (size_t i = 0; i < len; i++) {
if (!isprint(input[i])) {
return false;
}
}
embsec_sha256((const uint8_t *)input, len, hash);
return true;
}
4. Use Fresh Random Values¶
// BAD - predictable nonce
static uint32_t nonce = 0;
nonce++;
// GOOD - random nonce
uint8_t nonce[16];
embsec_random_bytes(nonce, sizeof(nonce));
Example: Secure Communication Protocol¶
#include <embsec/embsec.h>
#include <embsec/uart.h>
#include <embsec/crypto.h>
#include <string.h>
#define MSG_MAX_LEN 128
#define KEY_SIZE 32
#define NONCE_SIZE 16
typedef struct {
uint8_t nonce[NONCE_SIZE];
uint16_t len;
uint8_t data[MSG_MAX_LEN];
uint8_t mac[32];
} secure_message_t;
// Pre-shared key (in real system, derive properly)
static const uint8_t shared_key[KEY_SIZE] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
bool send_secure_message(const char *message) {
secure_message_t msg;
size_t msg_len = strlen(message);
if (msg_len > MSG_MAX_LEN) {
embsec_printf("Message too long\n");
return false;
}
// Generate random nonce
embsec_random_bytes(msg.nonce, NONCE_SIZE);
// Copy message
msg.len = msg_len;
memcpy(msg.data, message, msg_len);
// Calculate MAC over nonce + len + data
embsec_sha256_ctx_t ctx;
embsec_sha256_init(&ctx);
embsec_sha256_update(&ctx, msg.nonce, NONCE_SIZE);
embsec_sha256_update(&ctx, (uint8_t *)&msg.len, sizeof(msg.len));
embsec_sha256_update(&ctx, msg.data, msg.len);
uint8_t hash[32];
embsec_sha256_final(&ctx, hash);
// Calculate HMAC of hash
embsec_hmac_sha256(shared_key, KEY_SIZE, hash, 32, msg.mac);
// Send message
embsec_printf("SECURE_MSG:");
// Send as hex
char hex[65];
embsec_bytes_to_hex(msg.nonce, NONCE_SIZE, hex);
embsec_printf("%s:", hex);
embsec_printf("%04x:", msg.len);
for (size_t i = 0; i < msg.len; i++) {
embsec_printf("%02x", msg.data[i]);
}
embsec_printf(":");
embsec_bytes_to_hex(msg.mac, 32, hex);
embsec_printf("%s\n", hex);
return true;
}
bool verify_secure_message(const secure_message_t *msg) {
// Calculate expected MAC
embsec_sha256_ctx_t ctx;
embsec_sha256_init(&ctx);
embsec_sha256_update(&ctx, msg->nonce, NONCE_SIZE);
embsec_sha256_update(&ctx, (uint8_t *)&msg->len, sizeof(msg->len));
embsec_sha256_update(&ctx, msg->data, msg->len);
uint8_t hash[32];
embsec_sha256_final(&ctx, hash);
uint8_t expected_mac[32];
embsec_hmac_sha256(shared_key, KEY_SIZE, hash, 32, expected_mac);
// Verify MAC
if (embsec_crypto_memcmp(msg->mac, expected_mac, 32) != 0) {
embsec_printf("MAC verification failed\n");
return false;
}
// Display message
embsec_printf("Verified message: ");
for (size_t i = 0; i < msg->len; i++) {
embsec_putchar(msg->data[i]);
}
embsec_printf("\n");
return true;
}
int main(void) {
embsec_init();
embsec_printf("\n=== Secure Communication Demo ===\n");
embsec_printf("Commands:\n");
embsec_printf(" send <message> - Send secure message\n");
embsec_printf(" test - Run crypto tests\n\n");
char buffer[256];
while (1) {
embsec_printf("> ");
if (embsec_gets(buffer, sizeof(buffer))) {
if (strncmp(buffer, "send ", 5) == 0) {
send_secure_message(buffer + 5);
}
else if (strcmp(buffer, "test") == 0) {
// Test vectors
const char *test = "The quick brown fox jumps over the lazy dog";
uint8_t hash[32];
embsec_sha256((const uint8_t *)test, strlen(test), hash);
char hex[65];
embsec_bytes_to_hex(hash, 32, hex);
embsec_printf("SHA-256 test: %s\n", hex);
// Expected: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
const char *expected = "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592";
if (strcmp(hex, expected) == 0) {
embsec_printf("SHA-256 test PASSED\n");
} else {
embsec_printf("SHA-256 test FAILED\n");
}
}
else if (strlen(buffer) > 0) {
embsec_printf("Unknown command: %s\n", buffer);
}
}
}
}
This example demonstrates:
- Secure message format with nonce
- HMAC for authentication
- Proper use of crypto functions
- Input validation
- Test vectors for verification