Eventually, development will reach a point where you are ready to distribute your plugin or bot to other users. This tutorial will teach you how to do so.
A Word of Caution #
The DRM solution provided by gw2cc fulfills two objectives. Firstly, it forces end-users to purchase your product in order to use it and prevents them from just sharing it with each other. Secondly, it prevents attackers with malicious intent from accessing the script and project files within your package.
However, there are some caveats to this:
- The system is designed with the intent in mind to prevent a vast majority of script kiddies from circumventing it. However, in IT, nothing is 100% secure, and a very skilled attacker could eventually break it! This is just the reality we have to face. Despite this, we built in multiple layers of defense, and we consider it extremely unlikely that somebody is able to break it easily.
- Only your script and project files are sufficiently secured! This means that any resources, like images or .csv files, could potentially be read out by an attacker, even if it is not directly straightforward how to do so. Importantly, anything that you can do with these resources in a script, an attacker could potentially do as well. On the bright side, however, it is extremely difficult to crack your script or project files! See the Storing Secrets section for ways to safely store sensitive data.
- Since GDScript is a dynamic script language that provides a lot of introspection, attackers could potentially read out global variables of your scripts/nodes or call functions. This is insofar important as it means that any information stored in global variables is potentially accessible.
Generating a 256bit key #
If you run a Linux system or have git bash installed on Windows (which by default runs MinGW), you can simply generate a key by running:
openssl rand -hex 32
If OpenSSL is not available on your machine, you can also use the python script provided in the tools folder:
python tools/KeyGen.py
Storing Secrets #
As mentioned earlier, only your script files are truly protected. If you need to ship sensitive data with your plugin, for instance an API key, it is your own responsibility to properly secure it. This section describes various ways how this can be achieved.
In the following we assume that we want to store and use the string "my little secret" in our plugin without attackers being able to retrieve it. It acts as placeholder for more sensitive data, such as a JSON file, or an API key.
The File API provides methods to both write and read encrypted files. First generate a private key as described in Generating a 256bit key. Next we open a file in encrypted mode and write our secret to it:
var f = File.new()
var key = "AAAABBBBCCCCDDDDAAAABBBBCCCCDDDD"
f.open_encrypted("res://plugins/my_plugin/secret.txt", File.WRITE, key.to_ascii())
f.store_string("my little secret")
Now when we want to access the secret at runtime, we simply load it back:
func load_secret(key):
var f = File.new()
f.open_encrypted("res://plugins/my_plugin/secret.txt", File.READ, key.to_ascii())
var secret = f.get_as_text()
return secret
We can also further process the string in the method, e.g. by parsing JSON or filling in custom data structs.
When accessing the secret, care has to be taken, to never store the key or the retrieved secret in a global variable (or another member variable) but use it directly where it is required!
func work_with_secret():
var key = "AAAABBBBCCCCDDDDAAAABBBBCCCCDDDD" # local var is ok
var secret = load_secret(key) # local var is ok
print(secret)