Skip to main content

HMAC Auth SDK for C

C SDK for authenticating requests to the SafeSky API using HMAC-SHA256 signatures.

Click here to download the SDK

Installation

Dependencies:

  • OpenSSL (libssl-dev, libcrypto)
  • libcurl (for HTTP requests in examples)
  • libuuid (for UUID generation on Linux)
Ubuntu/Debian
sudo apt-get install libssl-dev libcurl4-openssl-dev uuid-dev
macOS (Homebrew)
brew install openssl curl ossp-uuid
CentOS/RHEL
sudo yum install openssl-devel libcurl-devel libuuid-devel

Compilation

## Compile the library and example
gcc -o example safesky_hmac_auth.c example.c -lssl -lcrypto -lcurl -luuid

## On macOS, you might need to specify OpenSSL path:
gcc -o example safesky_hmac_auth.c example.c \
    -I/opt/homebrew/opt/openssl/include \
    -L/opt/homebrew/opt/openssl/lib \
    -lssl -lcrypto -lcurl -luuid

## Run
./example

Quick Start

##include "safesky_hmac_auth.h"

int main() {
    // Your SafeSky API key
    const char* api_key = "YOUR_SAFESKY_API_KEY_HERE";
    
    // Generate authentication headers
    SafeSkyAuthHeaders headers;
    safesky_generate_auth_headers(
        api_key,
        "GET",
        "https://api.safesky.app/v1/uav?lat=50.6970&lng=4.3908",
        NULL,  // body is NULL for GET requests
        &headers
    );
    
    // Use headers.authorization, headers.x_ss_date, etc.
    printf("Authorization: %s\n", headers.authorization);
    printf("X-SS-Date: %s\n", headers.x_ss_date);
    printf("X-SS-Nonce: %s\n", headers.x_ss_nonce);
    printf("X-SS-Alg: %s\n", headers.x_ss_alg);
    
    return 0;
}

Usage Examples

The SDK includes 4 complete examples:

  1. GET nearby aircraft - Retrieve traffic in a specific area
  2. POST UAV position - Submit UAV telemetry data
  3. POST advisory (GeoJSON) - Publish GeoJSON FeatureCollection with polygon and point
  4. Manual step-by-step - Detailed authentication flow
Example 1: GET Request with libcurl
##include "safesky_hmac_auth.h"
##include <curl/curl.h>

void get_nearby_aircraft() {
    const char* api_key = "YOUR_SAFESKY_API_KEY_HERE";
    const char* url = "https://api.safesky.app/v1/uav?lat=50.6970&lng=4.3908&rad=20000";
    
    // Generate headers
    SafeSkyAuthHeaders headers;
    safesky_generate_auth_headers(api_key, "GET", url, NULL, &headers);
    
    // Setup libcurl
    CURL *curl = curl_easy_init();
    if (curl) {
        struct curl_slist *header_list = NULL;
        
        char auth_header[1024];
        snprintf(auth_header, sizeof(auth_header), "Authorization: %s", headers.authorization);
        header_list = curl_slist_append(header_list, auth_header);
        
        char date_header[256];
        snprintf(date_header, sizeof(date_header), "X-SS-Date: %s", headers.x_ss_date);
        header_list = curl_slist_append(header_list, date_header);
        
        char nonce_header[256];
        snprintf(nonce_header, sizeof(nonce_header), "X-SS-Nonce: %s", headers.x_ss_nonce);
        header_list = curl_slist_append(header_list, nonce_header);
        
        char alg_header[128];
        snprintf(alg_header, sizeof(alg_header), "X-SS-Alg: %s", headers.x_ss_alg);
        header_list = curl_slist_append(header_list, alg_header);
        
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
        
        CURLcode res = curl_easy_perform(curl);
        
        curl_slist_free_all(header_list);
        curl_easy_cleanup(curl);
    }
}
Example 2: POST Request
void post_uav_position() {
    const char* api_key = "YOUR_SAFESKY_API_KEY_HERE";
    const char* url = "https://api.safesky.app/v1/uav";
    
    // Build JSON body
    char body[1024];
    long timestamp = time(NULL);
    snprintf(body, sizeof(body),
        "[{\"id\":\"my_uav_001\",\"altitude\":110,\"latitude\":50.69378,"
        "\"longitude\":4.39201,\"last_update\":%ld,\"status\":\"AIRBORNE\","
        "\"call_sign\":\"Test UAV\",\"ground_speed\":10,\"course\":250,"
        "\"vertical_rate\":5}]", timestamp);
    
    // Generate headers (pass body for POST)
    SafeSkyAuthHeaders headers;
    safesky_generate_auth_headers(api_key, "POST", url, body, &headers);
    
    // Setup libcurl
    CURL *curl = curl_easy_init();
    if (curl) {
        struct curl_slist *header_list = NULL;
        
        // Add auth headers (same as GET example)
        // ... add all headers ...
        
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
        
        CURLcode res = curl_easy_perform(curl);
        
        curl_slist_free_all(header_list);
        curl_easy_cleanup(curl);
    }
}
Example 3: POST Advisory (GeoJSON)
void post_advisory() {
    const char* api_key = "YOUR_SAFESKY_API_KEY_HERE";
    const char* url = "https://api.safesky.app/v1/advisory";
    
    // Build GeoJSON FeatureCollection with polygon and point
    long timestamp = time(NULL);
    char body[8192];
    snprintf(body, sizeof(body),
        "{\"type\":\"FeatureCollection\",\"features\":["
        "{\"type\":\"Feature\",\"properties\":{"
        "\"id\":\"my_advisory_id1\",\"max_altitude\":111,\"last_update\":%ld,"
        "\"call_sign\":\"Advisory test with polygon\",\"remarks\":\"Inspection powerlines\"},"
        "\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[["
        "[4.3948,50.6831],[4.3952,50.6832],[4.3958,50.6835],"
        "/* ... more coordinates ... */"
        "[4.3948,50.6831]"
        "]]}},"
        "{\"type\":\"Feature\",\"properties\":{"
        "\"id\":\"my_advisory_id2\",\"max_altitude\":150,\"max_distance\":500,"
        "\"last_update\":%ld,\"call_sign\":\"Advisory test with a point\","
        "\"remarks\":\"Inspection rails\"},"
        "\"geometry\":{\"type\":\"Point\",\"coordinates\":[4.4,50.7]}}"
        "]}", timestamp, timestamp);
    
    // Generate headers (pass body for POST)
    SafeSkyAuthHeaders headers;
    safesky_generate_auth_headers(api_key, "POST", url, body, &headers);
    
    // Make request with libcurl (similar to Example 2)
}
Example 4: Manual Step-by-Step
void manual_authentication() {
    const char* api_key = "YOUR_SAFESKY_API_KEY_HERE";
    
    // Step 1: Derive KID
    char kid[64];
    safesky_derive_kid(api_key, kid);
    printf("KID: %s\n", kid);
    
    // Step 2: Derive HMAC key
    unsigned char hmac_key[32];
    safesky_derive_hmac_key(api_key, hmac_key);
    
    // Step 3: Generate timestamp and nonce
    char timestamp[64], nonce[64];
    safesky_generate_timestamp(timestamp);
    safesky_generate_nonce(nonce);
    
    // Step 4: Build canonical request
    char canonical[4096];
    safesky_build_canonical_request(
        "GET", "/v1/uav", "lat=50.6970&lng=4.3908",
        "api.safesky.app", timestamp, nonce, "",
        canonical
    );
    
    // Step 5: Generate signature
    char signature[256];
    safesky_generate_signature(canonical, hmac_key, signature);
    printf("Signature: %s\n", signature);
}

API Reference

Main Function

######## safesky_generate_auth_headers()

Generates all HMAC authentication headers for a SafeSky API request.

void safesky_generate_auth_headers(
    const char* api_key,           // Your SafeSky API key
    const char* method,            // HTTP method (GET, POST, etc.)
    const char* url,               // Full URL with protocol and query string
    const char* body,              // Request body (NULL for GET)
    SafeSkyAuthHeaders* headers_out // Output structure
);

SafeSkyAuthHeaders structure:

typedef struct {
    char authorization[512];  // Authorization header
    char x_ss_date[64];      // X-SS-Date header
    char x_ss_nonce[64];     // X-SS-Nonce header
    char x_ss_alg[32];       // X-SS-Alg header
} SafeSkyAuthHeaders;
Lower-Level Functions

For advanced use cases:

  • safesky_derive_kid(api_key, kid_out) - Derives Key Identifier
  • safesky_derive_hmac_key(api_key, hmac_key_out) - Derives HMAC signing key
  • safesky_build_canonical_request(...) - Builds canonical request string
  • safesky_generate_signature(canonical_request, hmac_key, signature_out) - Generates signature
  • safesky_generate_nonce(nonce_out) - Generates UUID v4 nonce
  • safesky_generate_timestamp(timestamp_out) - Generates ISO8601 timestamp

Authentication Flow

  1. Derive KID: KID = base64url(SHA256("kid:" + api_key)[0:16])
  2. Derive HMAC Key: HKDF-SHA256 with salt and info strings
  3. Generate Nonce: UUID v4 for replay protection
  4. Generate Timestamp: ISO8601 format with milliseconds
  5. Build Canonical Request:
    METHOD
    /path
    query_string
    host:hostname:port
    x-ss-date:timestamp
    x-ss-nonce:nonce
    
    body_hash_sha256_hex
    
  6. Sign: HMAC-SHA256(hmac_key, canonical_request) → base64
  7. Build Headers: Authorization header with KID, signed headers, and signature

Makefile Example

Create a Makefile for easier compilation:

CC = gcc
CFLAGS = -Wall -O2
LIBS = -lssl -lcrypto -lcurl -luuid

## macOS specific
ifeq ($(shell uname), Darwin)
    CFLAGS += -I/opt/homebrew/opt/openssl/include
    LIBS += -L/opt/homebrew/opt/openssl/lib
endif

all: example

example: safesky_hmac_auth.c example.c
	$(CC) $(CFLAGS) -o example safesky_hmac_auth.c example.c $(LIBS)

clean:
	rm -f example *.o

.PHONY: all clean

Then simply run:

make
./example

Security Notes

  • Keep API keys secret: Never commit them to version control
  • Use environment variables: Store keys securely
  • HTTPS only: Always use HTTPS in production (api.safesky.app)
  • Replay protection: Server validates nonces within 15-minute window
  • Clock skew: Server allows ±5 minutes timestamp tolerance
  • Memory safety: Always allocate sufficient buffer sizes for outputs

Troubleshooting

Compilation Errors

OpenSSL not found:

## Ubuntu/Debian
sudo apt-get install libssl-dev

## macOS
brew install openssl
export CFLAGS="-I/opt/homebrew/opt/openssl/include"
export LDFLAGS="-L/opt/homebrew/opt/openssl/lib"

libcurl not found:

## Ubuntu/Debian
sudo apt-get install libcurl4-openssl-dev

## macOS
brew install curl

libuuid not found (Linux):

sudo apt-get install uuid-dev
Runtime Errors

Invalid Signature:

  • Check body is identical (no extra whitespace)
  • Verify URL includes all query parameters
  • Ensure host header matches URL (include port for non-standard ports)

Replay Attack Detected:

  • Nonce was already used within 15-minute window
  • Generate fresh nonce for each request

Timestamp Out of Range:

  • Server time differs by more than ±5 minutes
  • Synchronize system clock with NTP

Running Examples

## Compile
gcc -o example safesky_hmac_auth.c example.c -lssl -lcrypto -lcurl -luuid

## Make sure server is running on localhost:8080 for local testing
## or update API_ENDPOINT to https://api.safesky.app for production

./example

Support

For questions or issues:

  • Email: support@safesky.app
  • Documentation: https://docs.safesky.app