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:
- 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://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
requests libraryimport 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
urllibimport 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]
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://api.safesky.app for production
python example.py
Support
For questions or issues:
- Email: support@safesky.app
- Documentation: https://docs.safesky.app