DRM Utils is a utility library made for Typescript indended to help licensing your NodeJS software (e.g. Electron).
npm install drm-utils
In case you indend to use terminal command- kindly use
npm install drm-utils -g
instead, to install and register the binary globally.
- Working with License:
import { License } from 'drm-utils';
const PRIVATE_KEY = Secp256k1.utils.randomPrivateKey();
const PUBLIC_KEY = Secp256k1.getPublicKey(PRIVATE_KEY);
const license = await License.create('alex');
await license.sign(PRIVATE_KEY);
console.log("Your valid license key:", license.signedLicense); // 616c6578-6c3835327162-7dba8218000-3044022058abfe388905b2d3572b67bf6f0d10f3ef5787877ddbb80350733cb5c218ef26022045ba16a22a737cafe84552e54ccdccd545c6a70b486beabcfbab0f250967429f
console.log("Is license key valid?", license.validate(PUBLIC_KEY) ? 'YES' : 'NO'); // YES
- Working with DRM abstraction:
import { DRM } from 'drm-utils';
const ISSUER_ID = 'cucer';
const PUBLIC_KEY = '047c237e5dd2a2cb6d1d3176997e50240fcdaf40704b898eb46d22882c6048d8d77deafe1169c2dc6010241e3f7654a5a07a1cf22be38dd8907deafafa321e16ad'; // Secp256k1.getPublicKey(PRIVATE_KEY)
const ISSUERS_MAPPING = new Map().set(ISSUER_ID, PUBLIC_KEY); // add any issuers you'd like
// This creates a DRM abstraction with Env and File storage strategies
// and offline validation mechanism using the ISSUERS_MAPPING (e.g. you may implement loading from remote API)
// Using the defaults the license will attempt to be loaded from:
// 1. Environment variable "SYN_DRM_LICENSE"
// 2. The "syn_drm_license.lic" file in CWD (working directory)
const drm = await DRM.create(ISSUERS_MAPPING).loadLicense();
console.log('Has the license key been found?', drm.hasLicense ? 'YES' : 'NO'); // NO
// Set the license, e.g. input by user manually from UI or fetched from a server (the license.signedLicense)
// The store method will persist given license to both env and file storages as described above
const theLicenseStringFromInput = '6375636572-6c3835327162-68b4b7d0-3044022032de288bdfa6bd9975eb3fe119bdca2d1086ee00ef297ebb0fe193e95a04cb1602206588e0db8424c66a915c93458e4812d3048e37bac088f3ef433a8584148e58fa';
await drm.storeLicense(theLicenseStringFromInput);
// ...or if not willing to persist (beware- calling storeLicense() will persist it)
await drm.setLicense(theLicenseStringFromInput);
// To validate a license you can call
console.log('Is the license key valid?', await drm.validateLicense(/* optionally set license string or instance here */) ? 'YES' : 'NO'); // YES
Note that storage and validation strategies can be set in
DRM
, thus custom logic such as loading license from DB and check it online via some API can be easily implemented (by implementing customILicenseStorage
andILicenseValidator
).
- Interfaces for custom implementations:
export interface ILicenseStorage {
/**
* Load license key
* @param args Any parameters accepted
* @throws MisingLicenseError
*/
load(...args: any[]): License | Promise<License>;
/**
* Stores license. Optional implementation.
* @param license
*/
store(license: License): boolean | Promise<boolean>;
}
export interface ILicenseValidator {
/**
* Validates license
* @param license
*/
validate(license: License): boolean | Promise<boolean>;
}
- Using custom implementation:
const drm = new DRM(customStorage, customValidator).loadLicense();
- Generate Operator Key
Usage: drm-utils pk [options]
Options:
-h, --help display help for command
- Generate a license key
Usage: drm-utils generate [options] <machineID>
Arguments:
machineID A MachineID e.g. l852qb
Options:
-i, --issuer-id <issuer> Issuer ID (name) to be used
-v, --valid-until <date> Valid until date unless lifelong (e.g. "Sep 2025")
-k, --private-key <key> Operator PRIVATE key for signing the license (never stored)
-h, --help display help for command
- Sign a generated raw license
Usage: drm-utils sign [options] <license>
Arguments:
license NON signed license key, e.g. 616c6578-6c3835327162-7dba8218000
Options:
-k, --private-key <key> Operator PRIVATE key (never stored)
-h, --help display help for command
- Validate a license key (works with both signed and non-signed)
Usage: drm-utils validate [options] <license>
Arguments:
license Validate a license key (either signed or NON signed), e.g. 616c6578-6c3835327162-7dba8218000 OR
616c6578-6c3835327162-7dba8218000-3045022100c915f5f698f840f2cae76c97cd319e8db522593a1dbd273305d332037f8960030220165e9c67349353d4fcc061b4cc0f7b5ab912d7bb92e7eba7db78c622c9039d25
Options:
-p, --public-key <key> Operator PUBLIC key (for SIGNED licese only)
-s, --skip-datecheck Skip validating time validity (signature only)
-h, --help display help for command
Multi-Strategy License Key consists of libraries and utils to create and operate offline and online license keys on machines (think Electron).
License Key Standard: {hex(key-issuer-id)}-{hex(murmur2(machine-id))}-{hex(valid-until)}-{hex-sig}
-
key-issuer-id
- License Key Issuer ID, further mapped to thesecp256k1
public key to verify signature license key segment. It can be either offline (hardcoded onto config/env) or online (fetched from a license server). -
murmur2()
- MurmurHash2 is a non-cryptographic hash function suitable for general hash-based lookup/validation. It is fast and small in size thus suitable for it's purpose of the local machine ID validation against the dedicated license key segment. -
machine-id
- Local Machine ID generated crossplatform (for Win usesMachineGuid
from the registryHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography
, OXS-IOPlatformUUID
and Linux-/var/lib/dbus/machine-id
). -
valid-until
- Timestamp the license key is valid until. If you want the key to be untlimited in time, simply use someNumber.MAX_SAFE_INTEGER
(e.g. max value of the signed UINT32). -
hex-sig
- Signature is the main component used to validate the key. Details described below.
The signature is the hex representation of a secp256k1 signature following BIP0340 derived from license segments parameters (key-issuer-id
, machine-id
, machine-id
), hashed using sha256 standard as following:
deriv = sha256({hex(key-issuer-id)}-{hex(murmur2(machine-id))}-{hex(valid-until)})
sig = secp256k1.sign(deriv, privateKey) // privateKey- secp256k1 private key
hexSig = toHex(sig) // the generated hex-sig
The signature verifiable as following:
sig = fromHex(hexSig)
isValid = secp256k1.verify(sig, deriv, pubKey) // isValid - boolean, pubKey- public key derived from secp256k1 private key
Note that the hex representation of signature might be normalized/humanized e.g. by adding dashes every 32 bytes to visually separate/enhance the signature look.