Skip to content

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
#include <embsec/crypto.h>

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

void embsec_sha256(const uint8_t *data, size_t len, uint8_t hash[32]);

Calculate SHA-256 hash of data in a single operation.

Parameters:

  • data: Input data to hash
  • len: Length of input data
  • hash: 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

void embsec_sha256_init(embsec_sha256_ctx_t *ctx);

Initialize SHA-256 context for incremental hashing.

embsec_sha256_update

void embsec_sha256_update(embsec_sha256_ctx_t *ctx, const uint8_t *data, size_t len);

Add data to the hash computation.

embsec_sha256_final

void embsec_sha256_final(embsec_sha256_ctx_t *ctx, uint8_t hash[32]);

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 key
  • key_len: Length of key
  • data: Input data
  • data_len: Length of input data
  • hmac: 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

void embsec_random_bytes(uint8_t *buffer, size_t len);

Generate cryptographically secure random bytes.

Parameters:

  • buffer: Buffer to fill with random data
  • len: 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

void embsec_bytes_to_hex(const uint8_t *bytes, size_t len, char *hex);

Convert binary data to lowercase hexadecimal string.

Parameters:

  • bytes: Input binary data
  • len: Number of bytes to convert
  • hex: Output buffer (must be at least 2*len+1 bytes)

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

int embsec_hex_to_bytes(const char *hex, size_t hex_len, uint8_t *bytes);

Convert hexadecimal string to binary data.

Parameters:

  • hex: Input hex string (must be even length)
  • hex_len: Length of hex string
  • bytes: 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

int embsec_crypto_memcmp(const void *a, const void *b, size_t len);

Constant-time memory comparison to prevent timing attacks.

Parameters:

  • a: First buffer
  • b: Second buffer
  • len: 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

void embsec_secure_zero(void *ptr, size_t len);

Securely clear sensitive data from memory. This function is guaranteed not to be optimized out by the compiler.

Parameters:

  • ptr: Memory to clear
  • len: 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

  1. Reuse contexts for multiple operations:

    embsec_sha256_ctx_t ctx;
    for (int i = 0; i < count; i++) {
        embsec_sha256_init(&ctx);
        // ... hash data[i] ...
    }
    

  2. Process data in chunks:

    // Instead of loading entire file
    while ((len = read_chunk(buffer, sizeof(buffer))) > 0) {
        embsec_sha256_update(&ctx, buffer, len);
    }
    

  3. Cache computed values:

    static uint8_t cached_hash[32];
    static bool hash_valid = false;
    
    if (!hash_valid) {
        embsec_sha256(data, len, cached_hash);
        hash_valid = true;
    }
    

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