183 lines
7.1 KiB
Markdown
183 lines
7.1 KiB
Markdown
# License server
|
|
|
|
This is reference implementation of license server that allows per-machine software licensing while it also manages software and data updates for the
|
|
client machines. The protocol is described below.
|
|
|
|
## Preactivation request
|
|
|
|
When application is run for first time it is not activated. First it sends its system params (Fletcher64 hashes) to the server to check
|
|
if the computer has not been preactivated
|
|
|
|
```http
|
|
POST /activate0 HTTP/1.1
|
|
```
|
|
|
|
Example request body:
|
|
```json
|
|
{
|
|
"appId": "coc",
|
|
"systemParams": {
|
|
"biosSerialNum": "8690a8fb436070a9",
|
|
"computerUUID": "13cfc3b6f8f7fdd2",
|
|
"diskSerialNum": "63a58b9728485155",
|
|
"nicMac": "4b2856a1e9e8f43e",
|
|
"osId": "ec4fe2f3023d1f21"
|
|
}
|
|
}
|
|
```
|
|
|
|
The server checks if the system is preactivated by checking if any of the system parameters exists in `PreactivationParams` table.
|
|
For example for license `XXXX-YYYY` there can be single entry for `biosSerialNum` parameters which matches the system of the requestor.
|
|
Therefore the activation will happen using the license number `XXXX-YYYY` and the system will response with license file that the client
|
|
system should store locally. We will describe the structure later.
|
|
|
|
Example Response body:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"licenseFile": "fG2HUWhE5kT10Ono3qgO/pzHNU1KAnUlEszz3B1pP5FdSQlIukg3P3xUgfHDQX1OBuAFH68WeXe2T0YP1dbjSsAyDJfpUltJnncwMMLOkfR3YbvyAVNmScgASLWwyQxQAVID6GOQ2weVNo3tAcbj2Ted7rx0HL36seSzyY5xuV/SnfJN6q5acqyJmibQcsrPQLZBvIb00Cy9KENnkVFVH70kC406pKxVZ3Ghqg8vTxgBK6sdbwqd7XrpupcQ4frwwm/NPesCIBYRG+6C/9oMroQoPZ+NEzOYQq2PADOZgOSvaMp4FUNe30IqNckobQO7N2TmW4BJDKVzhG5OYQRfs+xvzG0lF5gOBtmBhDf3yStgJj++jc2pNwe0rDSP2J+Yjs+V8wepyuCJ08cfZOh1feqnzmAarzAD80W8wRFd9U2lHDsyFt7Ke/3aPX454jxqITn/Wu6MK0paX8YAATWWAONyUsWTdA3UkJm45gNHJibcQcZLwKErZEPr2XBNy8nrImHRGFFtr3KC"
|
|
}
|
|
```
|
|
|
|
If the Preactivation is not available for given client, the system will response with `{ success: false }`
|
|
|
|
## Activation request
|
|
|
|
If the system is not preactivated the client application should ask use to enter the license key (Or the license key should be somehow
|
|
received by the client system. The license key is 24 characters in base32 (RFC 4648) format. For simplicity it can be sent to user as
|
|
e.g. `T3HZ-IFAT-HLN5-2I57-HAGL-V24R` which is easy to read and enter manually. Dashes in between the characters are ignored.
|
|
|
|
The client is supposed to send JSON request similar as in preactivation with just one parameter added `licenseNumber` which should be
|
|
uppercase without dashes so the server can directly query the database.
|
|
|
|
### Example
|
|
```http
|
|
POST /activate HTTP/1.1
|
|
Host: localhost:3000
|
|
Accept: application/json
|
|
Content-Type: application/json
|
|
Content-Length: 219
|
|
Charsets: utf-8
|
|
```
|
|
|
|
Request data:
|
|
```json
|
|
{
|
|
"appId": "coc",
|
|
"systemParams": {
|
|
"biosSerialNum": "8690a8fb436070a9",
|
|
"computerUUID": "13cfc3b6f8f7fdd2",
|
|
"diskSerialNum": "63a58b9728485155",
|
|
"nicMac": "4b2856a1e9e8f43e",
|
|
"osId": "ec4fe2f3023d1f21"
|
|
},
|
|
"licenseNumber": "JK33BTBSBKSKV63YEVLMQMBZ"
|
|
}
|
|
```
|
|
|
|
Response:
|
|
```
|
|
{
|
|
"success": true,
|
|
"licenseFile": "CmiN19MDaeCtA4L+cqhrnL71GWjBqTA6cqP8pUyfwRY+r/s/CyODkgHnO9eg3yaLLNxnNFMOnZOtxgtz8hNMNUIsTAKzus068sz9dJhV2yLrmkvhi1KjEJdOua4ZXuKSzjGKxM+VFXokFfFTqxvVpPt5sMkwq9kG/cZSwpBw7POhR+ncHeF11jjkbKVUnVgjGq8EDHDQFANYAVB3qbo7PY9CG3Gm25nORMUMpqwKieadVmklBZYs09EUqqwxAxxpD44Hw2DaRwoaVMuKTC//wH+3oS3zoL2mx+panJ/HPCN7ZtdBje+v6HSlfUoHgCHpFrr0+9/YqvxAQBWz0Q8dSyzyHkuzEkb4Ob7uWSeVhmKJ1TfzX8CAchth9f4CLCuLdzmCpRVvIAM6ZS4o0t3n/3AAJAmBuKFr9OrHTnN1EgXUYB1TVjiHqvBXR1jEyvYyvkAC4CTel7/Y1LRK2y6mq5is/uLcuKyKpWItMr5p9/3qJAove7tZpT1KrNLYaIPGudIwcnYin8EBAK09uUrqRg=="
|
|
}
|
|
```
|
|
|
|
## Check for updates
|
|
|
|
Another functionality that server supports is check for updates.
|
|
Client should send versions of its application modules (e.g. application, data) together with its activation ID, system parameters.
|
|
Server checks whether the system with given activation ID and system parameters is still valid and if yes it looks for updates
|
|
of the licensed modules. The typical request should look like this:
|
|
|
|
```json
|
|
{
|
|
"systemParams": {
|
|
"biosSerialNum": "8690a8fb436070a9",
|
|
"computerUUID": "13cfc3b6f8f7fdd2",
|
|
"diskSerialNum": "63a58b9728485155",
|
|
"nicMac": "4b2856a1e9e8f43e",
|
|
"osId": "ec4fe2f3023d1f21"
|
|
},
|
|
"activationId": "f0d68f64-4bc5-33b0-6ab3-e9b446baea08",
|
|
"moduleVersions": {
|
|
"coc-testdata": 2
|
|
}
|
|
}
|
|
```
|
|
|
|
The response contains `success: true` if the system is still activated with supplied activation number and system parameters.
|
|
|
|
If the number of licensed modules has changed since the activation, the server can reactivate the installation and return back also new `licenseFile`.
|
|
|
|
If there are newer versions available there should be list of updates attached. Here is the explanation of update parameters that are not obvious:
|
|
* flag - bit flag (bit 1: signals whether it is incremental update, bit 2: signals whether restart is required after update)
|
|
* checksum - SHA256 checksum of the zip file
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"moduleUpdates": [
|
|
{
|
|
"moduleId": "coc-testdata",
|
|
"version": 3,
|
|
"flag": 0,
|
|
"checksum": "6c878854d349752eceb0d52658e8838c2ae3cca53962c942a276e8944da25731",
|
|
"updateUri": "http://localhost:3000/static/testsite-v3.zip",
|
|
"instPath": "data"
|
|
},
|
|
{
|
|
"moduleId": "coc-testdata",
|
|
"version": 4,
|
|
"flag": 1,
|
|
"checksum": "a515353daae35dc1b3e9e06e52b95a53690984cc3172bb4e6b44c6b516afa040",
|
|
"updateUri": "http://localhost:3000/static/testsite-v4-incremental.zip",
|
|
"instPath": "data"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
|
|
|
|
## License file
|
|
|
|
License file is zlib deflated and AES256-GCM encrypted of following structure
|
|
|
|
```json
|
|
{
|
|
"data": "{\"activationId\":\"e8cb99bc-9827-5c3a-b944-94ac97c81366\",\"appId\":\"coc\",\"systemParams\":{\"biosSerialNum\":\"8690a8fb436070a9\",\"computerUUID\":\"13cfc3b6f8f7fdd2\",\"diskSerialNum\":\"63a58b9728485155\",\"nicMac\":\"4b2856a1e9e8f43e\",\"osId\":\"ec4fe2f3023d1f21\"},\"licensedModules\":[\"coc-engine\",\"coc-testdata\"],\"nonce\":\"pSvFZJ8q3tqBzp0pLEmCSg==\"}",
|
|
"signature": "304c02240163a4b1a6e9a366672df1a17418ab44fb3471fefc4234b4220de8079d0a59ee3c05ce3502240346e0d66d388e3f6cef1cffe14c6be6930a858c72190359fa57aa755c5767b3688d9d88"
|
|
}
|
|
```
|
|
|
|
The signature is ECDSA+SHA256 signature of the serialized data node. The data node extracts further below:
|
|
|
|
```json
|
|
{
|
|
"activationId": "e8cb99bc-9827-5c3a-b944-94ac97c81366",
|
|
"appId": "coc",
|
|
"systemParams": {
|
|
"biosSerialNum": "8690a8fb436070a9",
|
|
"computerUUID": "13cfc3b6f8f7fdd2",
|
|
"diskSerialNum": "63a58b9728485155",
|
|
"nicMac": "4b2856a1e9e8f43e",
|
|
"osId": "ec4fe2f3023d1f21"
|
|
},
|
|
"licensedModules": [
|
|
"coc-engine",
|
|
"coc-testdata"
|
|
]
|
|
}
|
|
```
|
|
|
|
As you can see the license file specifies activation number with all system parameters the license is activated to.
|
|
It also lists modules that are licensed for the given system.
|
|
Client system should save the license file received from the server localy and should upon every start check whether
|
|
the system parameters are the same as specified in the license file. The license file is protected from tempering by
|
|
ECDSA (public/private key cryptography) signature while the client only knows the public key of the server.
|
|
|
|
|
|
## Data model
|
|
|