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:
- GET nearby aircraft - Retrieve traffic in a specific area
- POST UAV position - Submit UAV telemetry data
- POST advisory (GeoJSON) - Publish GeoJSON FeatureCollection with polygon and point
- 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 Identifiersafesky_derive_hmac_key(api_key, hmac_key_out)- Derives HMAC signing keysafesky_build_canonical_request(...)- Builds canonical request stringsafesky_generate_signature(canonical_request, hmac_key, signature_out)- Generates signaturesafesky_generate_nonce(nonce_out)- Generates UUID v4 noncesafesky_generate_timestamp(timestamp_out)- Generates ISO8601 timestamp
Authentication Flow
- Derive KID:
KID = base64url(SHA256("kid:" + api_key)[0:16]) - Derive HMAC Key: HKDF-SHA256 with salt and info strings
- Generate Nonce: UUID v4 for replay protection
- Generate Timestamp: ISO8601 format with milliseconds
- Build Canonical Request:
METHOD /path query_string host:hostname:port x-ss-date:timestamp x-ss-nonce:nonce body_hash_sha256_hex - Sign:
HMAC-SHA256(hmac_key, canonical_request)→ base64 - 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