Signing and Key Storage¶
The Scorbit SDK uses ECDSA P-2561 for signing authentication requests. This can be implemented either through a TPM2 or using some other form of encrypted, secure storage.
To ensure that a game talking to Scorbit is authentic and originating traffic only from the machine that it claims to be, we use a key pair3 shared between Scorbit and the provider. Specifically, the public key is shared with Scorbit, while the secret key is stored securely in the game code or within a TPM.
A TPM chip is a convenient way to store these keys in a safe, secure manner, and is often used when a manufacturer provisions games off the assembly line. Most often, a TPM is either on the MPU board of the game or added later to the game through the use of a USB key or WiFi module.
In order to communicate with the API, the SDK must first authenticate using a digitally signed4 nonce. The nonce is a unique number used to prevent replay attacks, and it is cryptographically signed to prove authenticity. This signing process is handled internally by the SDK using an encrypted key.
Implementing Authentication¶
The SDK uses an encrypted key for authentication, which is generated from your private key using the encrypt_tool
. This approach enhances security by eliminating the need for the SDK to have direct access to the private key.
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.
1. Create the game state with encrypted key:¶
This example illustrates how to create a game state object using an encrypted key. The encrypted key is generated from your private key using the encrypt_tool
.
scorbit::DeviceInfo device_info;
device_info.provider = "scorbitron"; // Mandatory: provider name
device_info.machineId = 4419; // Mandatory: Scorbit-assigned Machine ID
device_info.gameCodeVersion = "1.12.3"; // Mandatory: your game version
device_info.hostname = "production"; // Optional: server hostname
device_info.uuid = ""; // Optional: device UUID
device_info.serialNumber = 0; // Optional: device serial number
// Use encrypt_tool to generate your encrypted key from your private key
std::string encrypted_key = "8qWNpMPeO1AbgcoPSsdeUORGmO/hyB70oyrpFyRlYWbaVx4Kuan0CAGaXZWS3JWdgmPL7p9k3UFTwAp5y16L8O1tYaHLGkW4p/yWmA==";
// Create game state object with encrypted key
scorbit::GameState gs = scorbit::createGameState(encrypted_key, std::move(device_info));
sb_device_info_t device_info = {
.provider = "scorbitron", // Mandatory: provider name
.machine_id = 4419, // Mandatory: Scorbit-assigned Machine ID
.game_code_version = "1.12.3", // Mandatory: your game version
.hostname = "production", // Optional: server hostname
.uuid = NULL, // Optional: device UUID
.serial_number = 0 // Optional: device serial number
};
// Use encrypt_tool to generate your encrypted key from your private key
const char* encrypted_key = "8qWNpMPeO1AbgcoPSsdeUORGmO/hyB70oyrpFyRlYWbaVx4Kuan0CAGaXZWS3JWdgmPL7p9k3UFTwAp5y16L8O1tYaHLGkW4p/yWmA==";
// Create game state object with encrypted key
sb_game_handle_t gs = sb_create_game_state2(encrypted_key, &device_info);
from scorbit import scorbit
info = scorbit.DeviceInfo()
info.provider = "scorbitron" # Mandatory: provider name
info.machine_id = 4419 # Mandatory: Scorbit-assigned Machine ID
info.game_code_version = "1.12.3" # Mandatory: your game version
info.hostname = "production" # Optional: server hostname
info.uuid = "" # Optional: device UUID
info.serial_number = 0 # Optional: device serial number
# Use encrypt_tool to generate your encrypted key from your private key
encrypted_key = '8qWNpMPeO1AbgcoPSsdeUORGmO/hyB70oyrpFyRlYWbaVx4Kuan0CAGaXZWS3JWdgmPL7p9k3UFTwAp5y16L8O1tYaHLGkW4p/yWmA=='
gs = scorbit.create_game_state(encrypted_key, info)
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.
Signature Format
Signatures must be DER-encoded ECDSA signatures.
Best Practices¶
1. Key Storage¶
- Use secure storage for your private key and preferably a TPM to store it
- Generate encrypted keys using the encrypt_tool before deployment
- Never commit private keys to source control
2. TPM Integration¶
- If possible, use TPM for production environments
- Implement proper error handling
-
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 ↩