licensify 4.0.0
licensify: ^4.0.0 copied to clipboard
A modern Dart library for secure license generation and validation using PASETO v4.
Licensify #
Licensify is a Dart library for issuing and validating software licenses backed by PASETO v4 tokens. It provides a strongly typed façade over the cryptography primitives in paseto_dart, making it straightforward to build licensing workflows that remain verifiable and tamper resistant.
Contents #
- Overview
- Key capabilities
- Quick start
- Data encryption
- Key lifecycle requirements
- Security guidance
- API reference
- License
Overview #
Licensify encapsulates key generation, license issuance, validation, and symmetric encryption in a single static API. Every license is represented as a signed PASETO v4 token with an authenticated payload containing application, expiry, feature, and metadata details. The library is designed for server-side issuance and on-device or in-application validation scenarios where deterministic and verifiable behavior is critical.
Key capabilities #
Licensing workflow #
- Generate Ed25519 key pairs for signing and verifying licenses.
- Assemble signed license tokens with strongly typed payload helpers.
- Validate licenses using keys or raw key bytes, exposing structured validation results.
- Inspect validated license metadata through the
Licensedomain object.
Cryptography support #
- Convert between key objects and PASERK representations (
k4.local,k4.local-pw,k4.local-wrap,k4.seal). - Derive symmetric keys from passwords using Argon2id with configurable parameters.
- Encrypt and decrypt structured data using XChaCha20-Poly1305 (PASETO v4.local) tokens.
Developer ergonomics #
- Asynchronous API surfaces for IO-bound cryptographic operations.
- Deterministic exceptions for malformed tokens and unsupported payloads.
- Memory management helpers to ensure explicit key disposal.
Quick start #
Generate signing keys and create a license #
import 'package:licensify/licensify.dart';
Future<void> issueLicense() async {
final keyPair = await Licensify.generateSigningKeys();
try {
final license = await Licensify.createLicense(
privateKey: keyPair.privateKey,
appId: 'com.example.product',
expirationDate: DateTime.now().add(const Duration(days: 365)),
type: LicenseType.pro,
features: const {
'analytics': true,
'api_access': true,
'max_users': 100,
},
metadata: const {
'customer': 'Example Corp',
'license_id': 'LIC-2025-001',
},
);
// Persist the license token in your licensing backend or provisioning flow.
print('License token: ${license.token}');
} finally {
keyPair.privateKey.dispose();
keyPair.publicKey.dispose();
}
}
Validate a license #
Future<void> validateLicense() async {
final keyPair = await Licensify.generateSigningKeys();
try {
final license = await Licensify.createLicense(
privateKey: keyPair.privateKey,
appId: 'com.example.product',
expirationDate: DateTime.now().add(const Duration(days: 30)),
);
final validation = await Licensify.validateLicense(
license: license,
publicKey: keyPair.publicKey,
);
if (!validation.isValid) {
throw StateError('License rejected: ${validation.message}');
}
final appId = await license.appId;
final features = await license.features;
final metadata = await license.metadata;
print('Validated license for: ' + appId);
print('Features: ' + features.toString());
print('Metadata: ' + (metadata ?? {}).toString());
} finally {
keyPair.privateKey.dispose();
keyPair.publicKey.dispose();
}
}
Detect tampering attempts #
Future<bool> isTamperedLicenseRejected() async {
final legitimateKeys = await Licensify.generateSigningKeys();
final attackerKeys = await Licensify.generateSigningKeys();
try {
final forgedLicense = await Licensify.createLicense(
privateKey: attackerKeys.privateKey,
appId: 'com.example.product',
expirationDate: DateTime.now().add(const Duration(days: 999)),
type: LicenseType.enterprise,
features: const {'max_users': 9999},
);
final result = await Licensify.validateLicense(
license: forgedLicense,
publicKey: legitimateKeys.publicKey,
);
return !result.isValid;
} finally {
attackerKeys.privateKey.dispose();
attackerKeys.publicKey.dispose();
legitimateKeys.privateKey.dispose();
legitimateKeys.publicKey.dispose();
}
}
Data encryption #
Future<Map<String, dynamic>> encryptAndDecrypt() async {
final encryptionKey = Licensify.generateEncryptionKey();
try {
final encryptedToken = await Licensify.encryptData(
data: const {
'user_id': 'user_123',
'permissions': ['read', 'write', 'admin'],
},
encryptionKey: encryptionKey,
);
final decrypted = await Licensify.decryptData(
encryptedToken: encryptedToken,
encryptionKey: encryptionKey,
);
return decrypted;
} finally {
encryptionKey.dispose();
}
}
Key lifecycle requirements #
Every LicensifyPrivateKey, LicensifyPublicKey, and LicensifySymmetricKey holds sensitive material in memory. Keys must be disposed explicitly with .dispose() once an operation completes. Failing to dispose keys leaves confidential bytes resident in memory until garbage collection and violates the library's security model.
Security guidance #
- Issue short-lived licenses where possible and rely on revocation metadata in your backend.
- Store signing keys in hardware security modules or dedicated secrets managers; never distribute private keys with client applications.
- Persist salts required for password-derived keys alongside encrypted payloads and protect them with the same rigor as the data they guard.
- Audit logging should record both successful and failed validations for anomaly detection.
- Treat decrypted license payloads as sensitive data and limit their exposure within your application.
API reference #
// Key management
static Future<LicensifyKeyPair> generateSigningKeys();
static LicensifyKeyPair keysFromBytes({required List<int> privateKeyBytes, required List<int> publicKeyBytes});
static LicensifySymmetricKey generateEncryptionKey();
static Future<LicensifySymmetricKey> encryptionKeyFromPassword({...});
static LicensifySymmetricKey encryptionKeyFromBytes({required List<int> keyBytes});
static LicensifySymmetricKey encryptionKeyFromPaserk({required String paserk});
static String encryptionKeyToPaserk({required LicensifySymmetricKey key});
static String encryptionKeyIdentifier({required LicensifySymmetricKey key});
static Future<LicensifySymmetricKey> encryptionKeyFromPaserkPassword({...});
static Future<String> encryptionKeyToPaserkPassword({...});
static LicensifySymmetricKey encryptionKeyFromPaserkWrap({...});
static String encryptionKeyToPaserkWrap({...});
static Future<LicensifySymmetricKey> encryptionKeyFromPaserkSeal({...});
static String encryptionKeyToPaserkSeal({...});
static LicensifySalt generatePasswordSalt({int length = K4LocalPw.saltLength});
// License creation
static Future<License> createLicense({
required LicensifyPrivateKey privateKey,
required String appId,
required DateTime expirationDate,
LicenseType type = LicenseType.standard,
Map<String, dynamic> features = const {},
Map<String, dynamic>? metadata,
bool isTrial = false,
});
// License validation
static Future<License> fromToken({required String token, required LicensifyPublicKey publicKey});
static Future<License> fromTokenWithKeyBytes({...});
static Future<LicenseValidationResult> validateLicense({
required License license,
required LicensifyPublicKey publicKey,
});
static Future<LicenseValidationResult> validateLicenseWithKeyBytes({...});
// Data encryption
static Future<String> encryptData({required Map<String, dynamic> data, required LicensifySymmetricKey encryptionKey});
static Future<Map<String, dynamic>> decryptData({required String encryptedToken, required LicensifySymmetricKey encryptionKey});
Refer to the inline API documentation for parameter details and advanced usage notes.
License #
This project is distributed under the terms of the MIT license. See LICENSE for the full text.