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:
- GET nearby aircraft - Retrieve traffic in a specific area
- POST UAV position - Submit UAV telemetry data
- POST UAV with query parameters - Submit UAV and get nearby traffic
- POST advisory (GeoJSON) - Publish GeoJSON FeatureCollection with polygon and point
- 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 stringbody(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 keyderive_hmac_key(api_key)→bytes- Derives HMAC signing key using HKDFbuild_canonical_request(...)→str- Builds canonical request stringgenerate_signature(canonical_request, hmac_key)→str- Generates HMAC-SHA256 signaturegenerate_nonce()→str- Generates UUID v4 noncegenerate_timestamp()→str- Generates ISO8601 timestamp with milliseconds
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 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
Security Notes
- Keep API keys secret: Never commit them to version control
- Use environment variables: Store keys in
.envfiles - 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_orssk_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