Provisioning, Signing and Keys¶
The Scorbit SDK uses a two-step key system to authenticate your machine with the Scorbit platform:
-
Provisioning key — A shared secret provided by Scorbit to your organization. You use the
encrypt_toolto generate an encrypted version of this key, which you embed in your game code or store on the device. The SDK uses this key to authenticate with the Scorbit API for the first time. -
Device key pair — During provisioning, the SDK exchanges the shared provisioning key for a unique key pair specific to that individual machine. This device key pair is what the SDK uses for all ongoing authentication. It must be stored persistently on the device (or on a TPM) so the machine can re-authenticate after reboots without re-provisioning.
The good news is that this process is handled automatically by the SDK. Your responsibilities are:
- Provide the encrypted provisioning key in the
Config - Implement key persistence callbacks so the SDK can save and load the device key pair (if there is no TPM)
Once provisioning completes, the device key pair is the only credential used for ongoing communication. The provisioning key is not used again unless the device needs to be re-provisioned (e.g., after a factory reset or key loss).
Device Identity¶
Each machine that connects to Scorbit must be uniquely identifiable. The SDK and the Scorbit API work together to establish a persistent identity for each device based on the provider, machine ID, and the unique device key pair generated during provisioning.
It is important that your provisioning key remains tied to your organization and that each device goes through its own provisioning to receive a unique key pair. If device keys are copied between machines, the platform cannot distinguish between them, which will cause pairing and session conflicts.
TPM (Trusted Platform Module)¶
A TPM2 chip provides a hardware-backed secure environment for storing cryptographic keys. This is the recommended approach for production machines, as it offers several advantages:
- Hardware-bound identity — keys cannot be extracted or copied from the TPM, ensuring one key per physical machine
- Tamper resistance — the TPM protects keys even if the storage media (SD card, hard drive) is replaced
- Simplified provisioning — manufacturers can provision keys onto the TPM during assembly, so the machine is ready to authenticate out of the box
A TPM is typically found on the MPU board of the game, or added later through a USB key or WiFi module.
Good news! The Scorbit Tap Pad not only provides a NFC capability for a user to identify themself, but also has a built in TPM chip. If the Tap Pad is detected, your key storage requirements are met automatically!
When TPM Is Not Available
If your hardware does not include a TPM, use the encrypt_tool to generate an encrypted provisioning key from your private key. The SDK will handle provisioning automatically. See Key Persistence for how to store the resulting device key pair.
Provisioning Key¶
The Scorbit SDK uses ECDSA P-2561 for signing authentication requests. The provisioning key is a shared secret between your organization and Scorbit. You generate an encrypted version of this key using the encrypt_tool provided with the SDK.
Key Generation
The encrypted key is generated using the encrypt_tool from your private key. This tool is provided with the SDK and should be used to generate the encrypted key before deploying your application.
Setting the provisioning key:¶
Provide the encrypted provisioning key in the Config before creating the game state:
sb_config_t config = sb_config_create();
sb_config_set_provider(config, "scorbitron");
sb_config_set_machine_id(config, 4419);
sb_config_set_game_code_version(config, "1.12.3");
sb_config_set_hostname(config, "production");
// Use encrypt_tool to generate your encrypted provisioning key from your private key
sb_config_set_encrypted_key(config,
"8qWNpMPeO1AbgcoPSsdeUORGmO/hyB70oyrpFyRlYWbaVx4Kuan0CAGaXZWS3JWdgmPL7p9k3UFTwAp5y16L8O1tYaHLGkW4p/yWmA==");
// Create game state object from config
sb_game_handle_t gs = sb_create_game_state(config);
sb_config_destroy(config);
// Use encrypt_tool to generate your encrypted provisioning key from your private key
std::string encrypted_key = "8qWNpMPeO1AbgcoPSsdeUORGmO/hyB70oyrpFyRlYWbaVx4Kuan0CAGaXZWS3JWdgmPL7p9k3UFTwAp5y16L8O1tYaHLGkW4p/yWmA==";
scorbit::Config config;
config.setProvider("scorbitron")
.setMachineId(4419)
.setGameCodeVersion("1.12.3")
.setHostname("production")
.setEncryptedKey(encrypted_key);
// Create game state object from config
auto gs = scorbit::createGameState(config);
from scorbit import scorbit
# Use encrypt_tool to generate your encrypted provisioning key from your private key
encrypted_key = '8qWNpMPeO1AbgcoPSsdeUORGmO/hyB70oyrpFyRlYWbaVx4Kuan0CAGaXZWS3JWdgmPL7p9k3UFTwAp5y16L8O1tYaHLGkW4p/yWmA=='
config = scorbit.Config()
config.set_provider("scorbitron")
config.set_machine_id(4419)
config.set_game_code_version("1.12.3")
config.set_hostname("production")
config.set_encrypted_key(encrypted_key)
# Create game state object from config
gs = scorbit.create_game_state(config)
Key Constants¶
| Constant | Value | Description |
|---|---|---|
SB_SIGNATURE_MAX_LENGTH |
72 | Maximum length of DER5-encoded ECDSA signature |
SB_KEY_LENGTH |
32 | Length of private key6 in bytes |
SB_DIGEST_LENGTH |
32 | Length of SHA-2567 digest in bytes |
Key Requirements¶
The SDK uses ECDSA P-256 with the following specifications:
| Component | Description | Specifications |
|---|---|---|
| Private Key | ECDSA P-2568 private key | - 32 bytes length - Must be securely stored |
| Encrypted Key | Base64-encoded encrypted private key | - Generated using encrypt_tool - Used for SDK initialization |
| Signature | DER-encoded9 ECDSA signature | - Maximum 72 bytes - Generated per auth request |
| Digest | SHA-25610 hash | - 32 bytes length - Provided by SDK |
Key Security
Never expose the private key11 in logs or debug output. Only use the encrypted key generated by the encrypt_tool.
Key Persistence¶
During provisioning, the SDK generates a unique device key pair for your machine. This key pair must be stored persistently so the device can authenticate on subsequent startups without re-provisioning.
To enable key persistence, register save and load callbacks on the Config object before creating the game state. The SDK will call your save callback when a new device key pair is generated, and your load callback on startup to retrieve a previously saved key pair.
The data passed to these callbacks is an opaque string — your application simply provides the storage mechanism, and the SDK handles the rest.
void save_key_callback(const char *key, void *user_data)
{
(void)user_data;
FILE *file = fopen("scorbit_key.txt", "w");
if (file) {
fprintf(file, "%s", key);
fclose(file);
}
}
int load_key_callback(char *buffer, size_t buffer_size, void *user_data)
{
(void)user_data;
FILE *file = fopen("scorbit_key.txt", "r");
if (!file)
return 0; // No key saved yet
size_t len = fread(buffer, 1, buffer_size - 1, file);
fclose(file);
if (len > 0) {
buffer[len] = '\0';
return (int)len;
}
return 0;
}
// Register on config before creating game state
sb_config_set_save_key_callback(config, save_key_callback, NULL);
sb_config_set_load_key_callback(config, load_key_callback, NULL);
const std::string KEY_FILE_PATH = "scorbit_key.txt";
void saveKeyCallback(const std::string &key)
{
std::ofstream file(KEY_FILE_PATH);
if (file.is_open()) {
file << key;
}
}
std::string loadKeyCallback()
{
std::ifstream file(KEY_FILE_PATH);
if (file.is_open()) {
return std::string((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
}
return ""; // No key saved yet
}
// Register on config before creating game state
config.setSaveKeyCallback(saveKeyCallback)
.setLoadKeyCallback(loadKeyCallback);
KEY_FILE_PATH = "scorbit_key.txt"
def save_key_callback(key):
with open(KEY_FILE_PATH, 'w') as f:
f.write(key)
def load_key_callback():
try:
with open(KEY_FILE_PATH, 'r') as f:
return f.read()
except FileNotFoundError:
return "" # No key saved yet
# Register on config before creating game state
config.set_save_key_callback(save_key_callback)
config.set_load_key_callback(load_key_callback)
Do Not Modify Key Data
The data passed to the save callback is an opaque string managed by the SDK. Do not interpret, modify, or log this data.
Storage Location
Choose a persistent, secure location on the device — for example, a file in a protected directory, EEPROM, or a secure enclave. The key must survive reboots and software updates. If the device key pair is lost, the SDK will re-provision using the provisioning key.
A Word about Network Time¶
Connecting an arcade game makes that game into an Internet of Things device (IoT), and like all IoT devices, it must be secure. Having accurate network time is an important piece of that security, which is why all modern operating system manages time automatically. If the base OS is Linux-based, or even Windows, this generally is managed by the system once connected. Batteries get old, and sometimes if time slips significantly it can cause authentication issues, so it's best to do a clean time sync on power up or reboot. While Scorbit will acquire time via the platform directly once connectivity is established, we recommend implementation of ntp, timesyncd, or chrony to prevent slippage and keep network transactions working smoothly.
If your system time differs more than 30 seconds beyond what the network time reports, the SDK will try to set correct system time. If application has enough privileges (i.e. root) it can set it successfully. If you find that authentication works at first, but then begins to fail, this is often the first place we look.
Best Practices¶
1. Key Storage¶
- Use a TPM (or Scorbit Tap Pad) for production environments whenever possible
- If a TPM is not available, generate encrypted provisioning keys using the
encrypt_toolbefore deployment - Never commit private keys to source control!
- Always implement key persistence callbacks — without them, the SDK must re-provision on every startup. If we detect this is happening, we will have no choice but to disable the key.
- Store all persisted key data in a location that survives reboots and software updates
2. Device Identity¶
- Ensure each physical machine goes through its own provisioning to receive a unique device key pair
- Do not copy device key data between machines!
- If a machine needs to be re-provisioned (e.g., after a factory reset), the SDK will handle it automatically using the provisioning key.
-
Elliptic Curve Digital Signature Algorithm using the P-256 curve ↩
-
Trusted Platform Module - A dedicated microcontroller for secure key storage ↩
-
A matched set of public and private cryptographic keys ↩
-
Cryptographically marked to prove authenticity ↩
-
Distinguished Encoding Rules - A standardized format for encoding data structures ↩
-
The secret part of a key pair, used for signing data ↩
-
Secure Hash Algorithm 2 - A cryptographic function that creates a 32-byte fingerprint of data ↩
-
An open-source cryptography library used for secure communications ↩
-
Elliptic Curve Key - A data structure for storing cryptographic keys ↩
-
A data type for handling very large numbers in cryptography ↩
-
A point on an elliptic curve used in cryptographic calculations ↩