Skip to content

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