Skip to content
Lithium is a licensing protocol which provides the ability to provide time locked, floating and leased licensing both over the internet and through an intranet server.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.vscode
resources
schema
src
.gitignore
.travis.yml
LICENSE
README.md

README.md

Lithium Build Status

Secure Asymmetric Licensing Protocol

Lithium is a licensing protocol which provides the ability to provide time locked, floating and leased licensing both over the internet and through an intranet server.

The primary design considerations with Lithium are that it should support licensing of a wide range of applications with varying requirements, it should enable clients to verify the integrity of a license without needing to communicate with a license server and should cater to both node-locked and floating license use cases.

Beyond that, it should be hardened against passive side-channel attacks, should be easily implemented on different platforms and should offer a reasonable degree of safety for general use cases. Active attacks such as binary manipulation are not covered under the scope of Lithium and should make use of an alternative strategy to protect distributed binaries against manipulation.

Architecture

License Data

Licenses are constructed out of a structured JSON object containing license metadata as well as a customizable payload segment. The payload segment is intended to be customized by the license consumer for toggling the availability of functionality and configuring options in the software.

{
    "meta": {
        "id": "8ddcb55cd6a341b48c4aa7b611e1721b",
        
        "activates": "2016-02-14T06:08:12.012Z",
        "expires": "2016-02-14T08:10:12.014Z"
    },
    
    "payload": {
        "feature1": true
    }
}

License Validation

Licenses are signed using asymmetric cryptographic signatures. These signatures are generated using the private key of the server generating the license, allowing the validity to be confirmed by checking the license's signature against the public key of that server. This ensures that the license originated from the specified server, while the server's published certificate chain enables clients to verify that the server's license originated from a trusted license source.

The server's certificate (public key) may be signed for a limited period of time, in which case licenses it generates will only remain valid for, at most, its validity period. Servers may also opt to only sign licenses for a portion of the server's validity period - enabling use cases like floating licenses or time-limited offline usage.

Signing Hierarchy

License Protection

License files are also encrypted to prevent them from being readable by anybody but the target machine. This is achieved by encrypting the license pack using the machine's public key, which is sent with the license request.

Locally, the machine's private key may be encrypted using machine specific data like processor codes, MAC addresses etc. Decisions about how the private key is protected fall outside the purview of this specification and will depend on the platform you are targetting.

License Packing

Licenses are packed using a sequence of PEM blocks. These blocks include the LITHIUM LICENSE KEY, LITHIUM LICENSE, LITHIUM SIGNATURE and LITHIUM CERTIFICATE blocks. With the exception of the LITHIUM CERTIFICATE blocks, ordering is not important.

-----BEGIN LITHIUM LICENSE KEY-----
6Ja6TUrx0euTsau/tjUoTfZm4QF9lC4uDLoWg6RoIZYmSF1zxjbmwysaxmYy13Dd
BrdNNz3f97TD5/t5MaEM56bGtpQj0dqgutP57Ue/kTqNADilcEgSvvR9LGhtNH4z
JGzbWDU3+TEWDyqUpIoxMHOitvg53u3O2aw99BpokeA=
-----END LITHIUM LICENSE KEY-----
-----BEGIN LITHIUM LICENSE-----
algorithm: aes256
iv: BAdz8wzMhcbiJyXpkaaXIQ==
9MAhZiUgECGOHgD9Lg2wJg5+grzdY9GsCctT6DYUMiUjicChIoov2aCAZevBXBXa
wiRpkmn0Bqgt525ZXgFakL1FlNFWgqnLR79Ij//J3aqU/TjP2oKoblhYLeHTY3vu
79uyIQvIyn6WRn3GoO3hVYFVPlWRXK2R9wd7pIj4yCGX1Ug=
-----END LITHIUM LICENSE-----
-----BEGIN LITHIUM SIGNATURE-----
algorithm: sha256
gFVD3kuzTLQYjLTvf6d3jyBZe9SFLz5Le4JLsCiVhd3CCwrnivWYOMwwtsdVJO+N
szVGbLOY6mttXNsP4So+Ucfy4xI0T3Gvz+afeiNCPnAZ2m0rOuqoxC+31vWMnuWt
JLKSMz587E06qHEk7I6/AuKWJBtFn0umQvhSNktr/ME=
-----END LITHIUM SIGNATURE-----
-----BEGIN LITHIUM CERTIFICATE-----
MIICsjCCAhugAwIBAgIBATANBgkqhkiG9w0BAQsFADB2MRkwFwYDVQQKExBTaWVy
cmEgU29mdHdvcmtzMRowGAYDVQQLExFMaXRoaXVtIExpY2Vuc2luZzEiMCAGA1UE
AxMZTGl0aGl1bSBUZXN0aW5nICh0ZXN0aW5nKTEZMBcGA1UEBRMQUm9vdCBDZXJ0
aWZpY2F0ZTAgFw0xNjA1MDMxNDE0MzRaGA8yMTE2MDQwOTE0MTQzNFowdjEZMBcG
A1UEChMQU2llcnJhIFNvZnR3b3JrczEaMBgGA1UECxMRTGl0aGl1bSBMaWNlbnNp
bmcxIjAgBgNVBAMTGUxpdGhpdW0gVGVzdGluZyAodGVzdGluZykxGTAXBgNVBAUT
EFJvb3QgQ2VydGlmaWNhdGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ+d
q1UwLUQaAm3rxC94PHiJkxwkgH1JSOE9NvFKYPOjGXVqAxpUUorvTkQLXnVS9tcB
Oe5kOKpsppluX7r8bkf78jAI2Pm447EtnMvHJKfRJqRqd3zmoL2goMIV21j9WuWj
Pjl6HPGsAvfdfSAvGZ18Huf4lEpj37HUL2gaPLKPAgMBAAGjTjBMMA4GA1UdDwEB
/wQEAwIB9jAPBgNVHSUECDAGBgRVHSUAMBMGA1UdEwEB/wQJMAcBAf8CAgCAMBQG
A1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOBgQAKjLSIOy1AFTNl
NessCe8ETzw3M2+gyH/IO7e2DFOYsflkkcaNERM9OA64afEQOgW+Klo9BGjYAB67
aZPcbxPanPD9ULUioF593phV4DufyW+qmfhWnAzQjHQpv8r2U8KXJdR02v7xEOg4
5lw1MYJgxleG6haeb6S3FUaEyi9hMA==
-----END LITHIUM CERTIFICATE-----
-----BEGIN LITHIUM CERTIFICATE-----
MIIClDCCAf2gAwIBAgIFAJW/VocwDQYJKoZIhvcNAQELBQAwdjEZMBcGA1UEChMQ
U2llcnJhIFNvZnR3b3JrczEaMBgGA1UECxMRTGl0aGl1bSBMaWNlbnNpbmcxIjAg
BgNVBAMTGUxpdGhpdW0gVGVzdGluZyAodGVzdGluZykxGTAXBgNVBAUTEFJvb3Qg
Q2VydGlmaWNhdGUwHhcNNzAwMTAxMDAwMDAwWhcNNzAwMTAxMDAwMDAwWjBqMRkw
FwYDVQQKExBTaWVycmEgU29mdHdvcmtzMRowGAYDVQQLExFMaXRoaXVtIExpY2Vu
c2luZzEiMCAGA1UEAxMZTGl0aGl1bSBUZXN0aW5nICh0ZXN0aW5nKTENMAsGA1UE
BRMEdGVzdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA8mFWK7JkzR/ELQmQ
3m3OjxOBmf8YcvK0wdJ+w1DzN+upKS6/E5sswRQGDf5YENHzA/WiesTyzHtVWTzh
qTlP4+KXyAFUcogjsf00DQPs/TUBOvlHzXxCVF9qQrdlmz1p9TXIbOED+m7wisrK
peJpBxEXAzGONBlxMEyEAqtkuC8CAwEAAaM6MDgwDgYDVR0PAQH/BAQDAgSQMBAG
A1UdEwEB/wQGMAQCAgCAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0B
AQsFAAOBgQCIPQ9hrI2AiHAYBsNbnpA1iN6hlx1DWCN6llAs7QwjPp8bR1hW7Yjn
A79u+KzYSLuKSd6lP3leaeNKkNl3JOrPeHeaJLMYL5TY09MwlID33+40/y3XWK8/
9aGgpXdxJ7IQv1MBZJZH2tELb4zyJzAquzeg54SuTvYdDm9MGBzbtw==
-----END LITHIUM CERTIFICATE-----

License Key

The license key block comprises an encrypted key for the decryption of the License block. This key is encrypted using an asymmetric encryption scheme like RSA and should only be decryptable by the node which will be making use of the license. This ensures that a license may not be used across multiple nodes while also making the license contents opaque to 3rd parties.

License

The license field contains the structured license object in encrypted form. It should be decrypted using the key sourced from the LITHIUM LICENSE KEY section, yielding a structured JSON object as defined by the license.data.schema.json schema.

The iv and algorithm headers exist to enable interoperability across various implementations. iv is encoded using standard Base64 encoding and is generated for each license encryption operation, while the algorithm will likely be aes256 in most cases.

Once the license has been confirmed to originate from a trusted source, the application is responsible for confirming that the current time rests between the activates and expires times. If it does not, the license is considered to have expired and the user should be informed.

Signature

The signature field represents the asymmetric cryptographic signature of the raw data within the LITHIUM LICENSE field (in encrypted form). By analyzing the signature it is therefore possible to determine whether the data has been tampered with.

If the signature does not match the expected signature for the data, given the public key available in the signature chain, then the certificate is considered tampered with and invalid. The application should inform the user to this effect.

Certificate

Each instance of the LITHIUM CERTIFICATE block represents a DER encoded x509 certificate. The ordering of these blocks is important, as the first block is expected to match the certificate embedded within the client application (a trusted root) while each subsequent certificate is expected to be signed by the previous certificate. In this way, it is possible to trace the authenticity of any certificate back to the root certificate.

Similarly, the final certificate is expected to be the certificate used to generate the signature for the license.

If any of these conditions is not met, the license is determined to be invalid and the application should inform the user to this effect.

Implementation Details

Clock Skew

Due to the nature of distributed systems, it is likely that the local machine's clock will have an offset from the license server's clock. When making a license request, the server should accept a reasonable clock skew parameter (a couple of minutes at most) and generate a license which caters for the target machine's clock time.

Alternative, though illadvised, approaches include storing a clock offset on the local machine as well as updating the local machine's time to match the license server's time. The former opens an attack surface, namely that the file may be modified to inject any desired offset and thereby extend a license indefinitely; the latter may not be possible across different platforms or may pose problems for certain classes of user or software.

Floating Licenses

Floating licenses, specifically those which work on a "seats" basis, are intended to be implemented through the use of continually renewed, short-lived licenses. These would be set to expire after between 60 seconds and 5 minutes, depending on the application. The application would request a license renewal once its local time approached a certain threshold from the current license's expiry time, for example - 30 seconds from expiry.

On the server side, licenses will only be generated when there are available license tokens in the pool or when a renewal request is received for a valid license. In the case of a renewal request, the expiry time for the relevant license (identified by its id is updated and sent to the requesting client). It is important to note that renewed licenses should only be generated for the client who previously checked out the license so as to prevent multiple nodes using the same license.

Licenses are returned to the pool when they expire, ensuring that offline users do not hold licenses which they are unable to use.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.