API Reference
YubiKey Components
Each YubiKey has:
- Key ID: 12 modhex characters (e.g.,
cccccccccccc) - AES Key: 16-byte secret key (hex or base64 encoded)
Any YubiKey OTP contains the Key ID as the first 12 characters:
ccccccccccccjktuvurlnlnvghubeukgkejrliudllkv
^^^^^^^^^^^^
Key ID = cccccccccccc
Use yubico-piv-tool -a status to get key ID from YubiKey device.
Key Formats
Key ID
- Length: 12 characters
- Characters:
cbdefghijklnrtuv(modhex only) - Example:
cccccccccccc
AES Key
- Length: 16 bytes
- Formats: Hex or Base64 encoded
- Hex Format: 32 hexadecimal characters (0-9, a-f, A-F)
- Example:
31323334353637383930313233343536 - Base64 Format: Standard or URL-safe base64 encoding
- Example:
MTIzNDU2Nzg5MDEyMzQ1Ng==(standard) - Example:
MTIzNDU2Nzg5MDEyMzQ1Ng(URL-safe)
The API automatically detects the format based on the input:
- 32 hex characters: treated as hex format
- Other formats: treated as base64 encoding
Use xxd and base64 commands to convert between formats if needed.
Format Conversion Examples
Convert hex to base64:
echo "31323334353637383930313233343536" | xxd -r -p | base64
# Output: MTIzNDU2Nzg5MDEyMzQ1Ng==
Convert base64 to hex:
echo "MTIzNDU2Nzg5MDEyMzQ1Ng==" | base64 -d | xxd -p -c 16
# Output: 31323334353637383930313233343536
Key ID must be exactly 12 modhex characters (cbdefghijklnrtuv)
Health Check
curl http://localhost:8002/health
Response:
{
"status": "healthy",
"database": {
"status": "connected"
}
}
Key Management
Add Key
Using Base64 Format
curl -X POST http://localhost:8002/api/v1/keys \
-H "Content-Type: application/json" \
-d '{
"key_id": "cccccccccccc",
"aes_key": "MTIzNDU2Nzg5MDEyMzQ1Ng==",
"description": "John Doe YubiKey"
}'
Using Hex Format
curl -X POST http://localhost:8002/api/v1/keys \
-H "Content-Type: application/json" \
-d '{
"key_id": "dddddddddddd",
"aes_key": "31323334353637383930313233343536",
"description": "Jane Doe YubiKey"
}'
Both examples use the same 16-byte AES key (1234567890123456) in different formats.
List Keys
curl http://localhost:8002/api/v1/keys
Response:
{
"status": "success",
"keys": [
{
"key_id": "cccccccccccc",
"description": "John Doe YubiKey",
"created_at": "2024-01-15T10:30:45Z",
"last_used": "2024-01-15T12:00:00Z",
"usage_count": 5
}
]
}
Delete Key
curl -X DELETE http://localhost:8002/api/v1/keys/cccccccccccc
OTP Validation
REST API
curl -X POST http://localhost:8002/api/v1/decrypt \
-H "Content-Type: application/json" \
-d '{"otp": "ccccccccccccjktuvurlnlnvghubeukgkejrliudllkv"}'
Success Response:
{
"status": "OK",
"key_id": "cccccccccccc",
"counter": 26,
"timestamp_low": 35599,
"timestamp_high": 15,
"session_use": 3,
"decrypted_at": "2024-01-15T10:30:45Z"
}
Error Response:
{
"status": "ERROR",
"error_code": "KEY_NOT_FOUND",
"message": "Key not found"
}
KSM Protocol (Legacy)
curl "http://localhost:8002/wsapi/decrypt/?otp=ccccccccccccjktuvurlnlnvghubeukgkejrliudllkv"
Success response:
OK counter=001a low=8b0f high=0f use=03
Error response:
ERR Key not found
Error Codes
| Code | Description |
|---|---|
INVALID_OTP | OTP format is invalid |
KEY_NOT_FOUND | Key not found |
REPLAY_DETECTED | OTP has already been used |
DECRYPTION_FAILED | OTP decryption failed |
INVALID_JSON | Request body contains invalid JSON |
MISSING_KEY_ID | Key ID field is required |
MISSING_AES_KEY | AES key field is required |
INVALID_KEY_ID_LENGTH | Key ID must be exactly 12 characters |
INVALID_KEY_ID_FORMAT | Key ID contains invalid modhex characters |
INVALID_AES_KEY_FORMAT | AES key format is invalid (not hex or base64) |
INVALID_AES_KEY_LENGTH | AES key must be exactly 16 bytes |
STORAGE_ERROR | Failed to store key in database |
Status Codes
| Code | Meaning |
|---|---|
200 | Success |
201 | Created |
400 | Bad Request |
404 | Not Found |
409 | Conflict (replay detected) |
422 | Unprocessable Entity (decryption failed) |
500 | Server Error |