Skip to main content

HMAC Auth SDK for Python

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

Click here to download the SDK

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-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://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://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://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://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://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://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://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://api.safesky.app for production

python example.py

Support

For questions or issues:

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