Skip to main content

HMAC Auth SDK for Python

SafeSky HMAC Authentication SDK for Python

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

Click here to download the SDK

API Endpoint: All requests use https://uav-api.safesky.app. Each API key is associated with either a sandbox or production environment. The gateway automatically routes your request to the correct backend based on the environment configured for your API key.

Installation

No external dependencies required! This SDK uses only Python standard library.

## Simply copy safesky_hmac_auth.py to your project

Requirements:

  • Python 3.6+

Quick Start

import safesky_hmac_auth as auth

## Your SafeSky API key
API_KEY = 'YOUR_SAFESKY_API_KEY_HERE'

## Generate authentication headers
headers = auth.generate_auth_headers(
    API_KEY,
    'GET',
    'https://sandbox-public-uav-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908'
    ## 'https://public-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908'  ## Production
)

## Use with your HTTP client
## headers contains: Authorization, X-SS-Date, X-SS-Nonce, X-SS-Alg

Usage Examples

Example 1: GET Request (Nearby Aircraft)
import http.client
import json
from urllib.parse import urlparse
import safesky_hmac_auth as auth

API_KEY = 'YOUR_SAFESKY_API_KEY_HERE'

## Generate headers
url = 'https://uav-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908&rad=20000'
headers = auth.generate_auth_headers(API_KEY, 'GET', url)

## Make request
parsed_url = urlparse(url)
conn = http.client.HTTPSConnection(parsed_url.hostname)
conn.request('GET', parsed_url.path + '?' + parsed_url.query, headers=headers)
response = conn.getresponse()
data = response.read().decode('utf-8')
print(json.dumps(json.loads(data), indent=2))
conn.close()
Example 2: POST Request (Publish UAV Position)
import json
import time
import safesky_hmac_auth as auth

API_KEY = 'YOUR_SAFESKY_API_KEY_HERE'

## Prepare body
body_data = [{
    "id": "my_uav_001",
    "altitude": 110,
    "latitude": 50.69378,
    "longitude": 4.39201,
    "last_update": int(time.time()),
    "status": "AIRBORNE",
    "call_sign": "Test UAV",
    "ground_speed": 10,
    "course": 250,
    "vertical_rate": 5
}]
body = json.dumps(body_data)

## Generate headers (pass body for POST)
url = 'https://uav-api.safesky.app/v1/uav'
headers = auth.generate_auth_headers(API_KEY, 'POST', url, body)
headers['Content-Type'] = 'application/json'

## Make request with body
conn = http.client.HTTPSConnection('api.safesky.app')
conn.request('POST', '/v1/uav', body=body, headers=headers)
response = conn.getresponse()
print(f'Status: {response.status}')
conn.close()

Complete Examples

The SDK includes 5 complete examples in example.py:

  1. GET nearby aircraft - Retrieve traffic in a specific area
  2. POST UAV position - Submit UAV telemetry data
  3. POST UAV with query parameters - Submit UAV and get nearby traffic
  4. POST advisory (GeoJSON) - Publish GeoJSON FeatureCollection with polygon and point
  5. Manual step-by-step - Detailed authentication flow
Example 3: POST with Query Parameters
import safesky_hmac_auth as auth

## URL includes query parameters
url = 'https://uav-api.safesky.app/v1/uav?return_nearby_traffic=true'
body = json.dumps([...])  ## Your UAV data

## Query params are automatically included in signature
headers = auth.generate_auth_headers(API_KEY, 'POST', url, body)
Example 4: POST Advisory (GeoJSON)
import safesky_hmac_auth as auth

body_data = {
    "type": "FeatureCollection",
    "features": [{
        "type": "Feature",
        "properties": {
            "id": "advisory_001",
            "max_altitude": 111,
            "last_update": int(time.time()),
            "call_sign": "Test Advisory",
            "remarks": "Testing"
        },
        "geometry": {
            "type": "Polygon",
            "coordinates": [[
                [4.38, 50.69],
                [4.40, 50.69],
                [4.40, 50.70],
                [4.38, 50.70],
                [4.38, 50.69]
            ]]
        }
    }]
}
body = json.dumps(body_data)

url = 'https://uav-api.safesky.app/v1/advisory'
headers = auth.generate_auth_headers(API_KEY, 'POST', url, body)
Example 5: Using with requests library
import requests
import safesky_hmac_auth as auth

API_KEY = 'YOUR_SAFESKY_API_KEY_HERE'

## GET request
url = 'https://uav-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908'
headers = auth.generate_auth_headers(API_KEY, 'GET', url)
response = requests.get(url, headers=headers)
print(response.json())

## POST request
body = json.dumps([...])
url = 'https://uav-api.safesky.app/v1/uav'
headers = auth.generate_auth_headers(API_KEY, 'POST', url, body)
headers['Content-Type'] = 'application/json'
response = requests.post(url, data=body, headers=headers)
print(response.status_code)
Example 6: Using with urllib
import urllib.request
import safesky_hmac_auth as auth

API_KEY = 'YOUR_SAFESKY_API_KEY_HERE'

url = 'https://uav-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908'
headers = auth.generate_auth_headers(API_KEY, 'GET', url)

req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req) as response:
    data = response.read().decode('utf-8')
    print(data)

API Reference

generate_auth_headers(api_key, method, url, body='')Dict[str, str]

Main function - Generates all HMAC authentication headers.

Parameters:

  • api_key (str): Your SafeSky API key (e.g., ssk_live_...)
  • method (str): HTTP method (GET, POST, etc.)
  • url (str): Full URL including protocol, host, path, and query string
  • body (str, optional): Request body for POST/PUT requests (default: empty string)

Returns: Dictionary with authentication headers:

{
    'Authorization': 'SS-HMAC Credential=...',
    'X-SS-Date': '2025-11-12T14:30:00.123Z',
    'X-SS-Nonce': 'uuid-v4',
    'X-SS-Alg': 'SS-HMAC-SHA256-V1'
}
Lower-Level Functions

For advanced use cases, you can use individual functions:

  • derive_kid(api_key)str - Derives Key Identifier from API key
  • derive_hmac_key(api_key)bytes - Derives HMAC signing key using HKDF
  • build_canonical_request(...)str - Builds canonical request string
  • generate_signature(canonical_request, hmac_key)str - Generates HMAC-SHA256 signature
  • generate_nonce()str - Generates UUID v4 nonce
  • generate_timestamp()str - Generates ISO8601 timestamp with milliseconds

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
    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

Security Notes

  • Keep API keys secret: Never commit them to version control
  • Use environment variables: Store keys in .env files
  • 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

Troubleshooting

Invalid Signature

Check:

  • Body is identical (no extra whitespace, encoding issues)
  • URL includes all query parameters
  • Host header matches URL (include port for non-standard ports)
Replay Attack Detected
  • Nonce was already used within 15-minute window
  • Generate a fresh nonce for each request
Timestamp Out of Range
  • Server time differs by more than ±5 minutes
  • Synchronize your system clock with NTP
Authentication Failed
  • Verify API key is correct (starts with ssk_live_ or ssk_test_)
  • Check API key has required permissions for the endpoint
  • Ensure all headers are present

Running Examples

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

python example.py

Support

For questions or issues:

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