High-level Session API for Status Keycard operations. Drop-in replacement for status-keycard-go.
This library provides a simple C API for keycard operations, designed to integrate seamlessly with status-desktop via Nim bindings.
Built on: keycard-qt - Low-level APDU library
┌──────────────────────────────────────────┐
│ status-desktop (Nim) │
│ Direct C function calls │
└──────────────┬───────────────────────────┘
│
┌──────────────▼───────────────────────────┐
│ status-keycard-qt (C API) │
│ • keycard_start() │
│ • keycard_authorize() │
│ • keycard_change_pin() │
│ • ... 15 functions │
└──────────────┬───────────────────────────┘
│
┌──────────────▼───────────────────────────┐
│ SessionManager (C++/Qt) │
│ • Card/reader monitoring │
│ • Auto-connection management │
│ • State machine │
│ • Pairing storage │
└──────────────┬───────────────────────────┘
│
┌──────────────▼───────────────────────────┐
│ keycard-qt │
│ • CommandSet (26 methods) │
│ • SecureChannel (AES-256) │
│ • KeycardChannel (PC/SC + NFC) │
└──────────────────────────────────────────┘
- Direct C API - Simple function calls, no JSON
- Session Management - Auto-detection, state machine, persistent pairing
- Signal System - Real-time status-changed notifications
- Cross-platform - Linux, macOS, Windows, Android, iOS
- Thread-safe - Safe for concurrent access
- Drop-in replacement - Compatible with nim-keycard-go
// Session management
KeycardResult* keycard_initialize(void);
KeycardResult* keycard_start(const char* storage_path, bool log_enabled, const char* log_path);
KeycardResult* keycard_stop(void);
KeycardResult* keycard_get_status(void);
// Card operations
KeycardResult* keycard_initialize_card(const char* pin, const char* puk, const char* pairing_password);
KeycardResult* keycard_authorize(const char* pin);
KeycardResult* keycard_change_pin(const char* new_pin);
KeycardResult* keycard_change_puk(const char* new_puk);
KeycardResult* keycard_unblock_pin(const char* puk, const char* new_pin);
// Key operations
KeycardResult* keycard_generate_mnemonic(int length);
KeycardResult* keycard_load_mnemonic(const char* mnemonic, const char* passphrase);
KeycardResult* keycard_export_login_keys(void);
KeycardResult* keycard_export_recover_keys(void);
// Metadata
KeycardResult* keycard_get_metadata(void);
KeycardResult* keycard_store_metadata(const char* name, const char** paths, int paths_count);
// Utilities
KeycardResult* keycard_factory_reset(void);
void keycard_free_result(KeycardResult* result);typedef struct {
bool success; // Operation succeeded
char* error; // Error message (NULL if success)
char* data; // JSON data for complex results
} KeycardResult;typedef void (*KeycardSignalCallback)(const char* signal);
void keycard_set_signal_callback(KeycardSignalCallback callback);Signal format:
{
"type": "status-changed",
"event": {
"state": "Ready",
"cardUID": "abc123",
"cardPresent": true,
"keyInitialized": true,
"pinRetryCount": 3,
"pukRetryCount": 5
}
}# Prerequisites
# 1. Build keycard-qt first
cd ../qt-keycard/build
cmake .. && make
# 2. Build status-keycard-qt
cd ../../status-keycard-qt
mkdir build && cd build
cmake ..
make- Qt 6.9.2+ (Core)
- OpenSSL 3.x
- keycard-qt (built)
- CMake 3.16+
- C++17 compiler
#include <status-keycard-qt/status_keycard.h>
#include <stdio.h>
void on_signal(const char* signal) {
printf("Signal: %s\n", signal);
}
int main() {
KeycardResult* result;
// Initialize
result = keycard_initialize();
if (!result->success) {
printf("Init failed: %s\n", result->error);
return 1;
}
keycard_free_result(result);
// Set callback
keycard_set_signal_callback(on_signal);
// Start service
result = keycard_start("./pairings.json", false, NULL);
if (!result->success) {
printf("Start failed: %s\n", result->error);
return 1;
}
keycard_free_result(result);
// Wait for card detection (via signals)...
// Authorize
result = keycard_authorize("123456");
if (result->success) {
printf("Authorized!\n");
} else {
printf("Auth failed: %s\n", result->error);
}
keycard_free_result(result);
// Cleanup
keycard_stop();
keycard_reset();
return 0;
}# keycard_go/impl.nim
type KeycardResult* = object
success*: bool
error*: cstring
data*: cstring
proc keycard_initialize*(): ptr KeycardResult {.importc.}
proc keycard_start*(storage_path: cstring, log_enabled: bool, log_path: cstring): ptr KeycardResult {.importc.}
proc keycard_authorize*(pin: cstring): ptr KeycardResult {.importc.}
proc keycard_free_result*(result: ptr KeycardResult) {.importc.}
type KeycardSignalCallback* = proc(signal: cstring): void {.cdecl.}
proc keycard_set_signal_callback*(callback: KeycardSignalCallback) {.importc.}# keycard_go.nim
import keycard_go/impl
proc keycardInitialize*(): string =
let res = keycard_initialize()
defer: keycard_free_result(res)
if not res.success:
return $res.error
return "ok"
proc keycardStart*(storagePath: string, logEnabled: bool = false): string =
let res = keycard_start(storagePath.cstring, logEnabled, nil)
defer: keycard_free_result(res)
if not res.success:
return $res.error
return "ok"
proc keycardAuthorize*(pin: string): tuple[ok: bool, authorized: bool] =
let res = keycard_authorize(pin.cstring)
defer: keycard_free_result(res)
if not res.success:
return (false, false)
# Parse res.data JSON to get authorized field
return (true, true) # simplified- keycard-qt - Low-level APDU library
- status-keycard-go - Original Go implementation
- nim-keycard-go - Nim bindings
- status-desktop - Status Desktop application