Saltar al contenido principal

💙 SDK Oficial para Flutter & Dart (Manual Técnico & API de Métodos)

El paquete valid_license es el SDK oficial de ValidLicence para aplicaciones móviles (Android, iOS) y de escritorio (Windows, macOS, Linux). Implementa validación criptográfica asimétrica basada en el algoritmo Ed25519 (RFC 8032) con capacidad de operación offline ininterrumpida y protección contra manipulación de hardware.


🏛️ 1. Estructura y Módulos del SDK​

valid_license/
├── lib/
│ ├── valid_license.dart # Exportador principal de la librería
│ └── src/
│ ├── valid_license_client.dart # Orquestador del ciclo de vida y verificación de licencias
│ ├── machine_id_provider.dart # Extracción determinista de la huella de hardware
│ ├── ed25519_signature_helper.dart # Motor criptográfico sobre curvas de Edwards (Curve25519)
│ └── valid_license_record.dart # Modelo de datos inmutable de la licencia

⚙️ 2. Módulo ValidLicenseClient (Clase Principal)​

Es el núcleo del SDK encargado de realizar la comunicación con el servidor VPS y gestionar el respaldo criptográfico local.

Constructor​

ValidLicenseClient({
required String serverUrl,
String? publicKeyHex,
String? hmacSecret,
String clientVersion = '2026.1',
})
ParámetroTipoRequeridoDescripción
serverUrlStringSíURL base del servidor VPS (ej: https://licencias.labtec.sbs).
publicKeyHexString?OpcionalClave pública Ed25519 de 32 bytes en formato Hex (64 caracteres) para verificación asimétrica sin secretos.
hmacSecretString?OpcionalSecreto simétrico para retrocompatibilidad con esquemas legacy HMAC-SHA256.
clientVersionStringNoVersión de compilación del software cliente (por defecto 2026.1).

Método getMachineId() (Estático)​

static Future<String> getMachineId()
  • Propósito: Retorna la huella única de hardware del equipo actual en formato REX-XXXX-XXXX-XXXX.
  • Comportamiento:
    • En Windows: Consulta HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid.
    • En Linux: Lee /etc/machine-id o /var/lib/dbus/machine-id.
    • En macOS: Consulta IOPlatformUUID vía ioreg.
    • Calcula un hash SHA-256 determinista y memoiza el resultado en memoria para evitar accesos repetitivos a disco.

Método verify() (Verificación Híbrida)​

Future<ValidLicenseRecord> verify({
String? companyNit,
String? companyName,
String? currentToken,
Map<String, dynamic>? customData,
List<String>? features,
bool isPrimaryHost = true,
void Function()? onRevoked,
String? customBaseUrl,
})

Parámetros:

  • companyNit: NIT o documento tributario de la empresa cliente.
  • companyName: Nombre comercial o razón social registrada.
  • currentToken: Token criptográfico almacenado localmente en la base de datos segura.
  • customData: Diccionario con metadatos personalizados (ej: {'sucursal': 'Norte', 'caja': 2}).
  • features: Lista de módulos requeridos para operar (ej: ['electronic_billing', 'ai_assistant']).
  • isPrimaryHost: Booleano que indica si el equipo actúa como servidor principal de la red local.
  • onRevoked: Función callback que se ejecuta inmediatamente si la licencia fue suspendida o eliminada desde el panel web del VPS.
  • customBaseUrl: Permite sobreescribir temporalmente el host para reintentos transparentes (HTTP/HTTPS).

Flujo Interno de Ejecución:

  1. Fase Online (Handshake HTTP):
    • Realiza un POST con timeout de 6 segundos a /api/v1/license/verify.
    • Si el servidor responde 200 OK, valida el estado. Si está marcado como isRevoked o status == 'DELETED', dispara onRevoked() y retorna un registro bloqueado.
    • Si el servidor entrega un nuevo token firmado, lo decodifica y verifica con la clave pública Ed25519.
  2. Fase Fallback Offline (Criptografía Pura):
    • Si la red falla o el servidor está en mantenimiento, analiza el currentToken local.
    • Valida matemáticamente que la firma corresponda a los datos y pertenezca al Machine ID del computador.
    • Si la firma es legítima y la fecha expiresAt es futura, autoriza el acceso normalmente.

Método decodeAndVerifyToken() (Estático)​

static Future<ValidLicenseRecord?> decodeAndVerifyToken(
String token, {
required String expectedMachineId,
String? publicKeyHex,
String? hmacSecret,
})
  • Propósito: Analiza la cadena del token (Base64URL.FirmaHex), extrae el payload JSON y valida la firma digital sin realizar conexiones de red.
  • Validación de Hardware: Si el token fue emitido para otra máquina (targetMachineId != expectedMachineId y targetMachineId != 'REX-UNIVERSAL-ALL'), el método rechaza el token retornando null.

🔑 3. Módulo Ed25519SignatureHelper​

Contiene la implementación criptográfica sobre la curva elíptica de Edwards (Curve25519):

Métodos del Helper​

  • getCanonicalString(Map<String, dynamic> payload): Genera la cadena canónica estandarizada concatenando los campos críticos:
    companyName|companyNit|machineId|plan|issuedAt|expiresAt|dianQuota|maxUsers
  • verifySignature(Map payload, String signature, {pubKeyHex, hmacSecret}):
    • Convierte los 64 caracteres de pubKeyHex en 32 bytes crudos.
    • Convierte los 128 caracteres de signature en 64 bytes de firma.
    • Ejecuta la validación estricta de la curva elíptica mediante el paquete cryptography.

📊 4. Módulo ValidLicenseRecord (Modelo Inmutable)​

Representa el estado completo y los privilegios autorizados de la empresa:

Propiedades y Getters​

PropiedadTipoDescripción
isActivebooltrue si el estado es ACTIVE y la fecha de expiración no ha pasado.
isUsableboolAlias de isActive para validaciones de arranque rápido.
isExpiredbooltrue si la fecha y hora actual del sistema superó expiresAt.
daysRemainingintNúmero entero de días restantes de servicio antes del vencimiento.
companyNameString?Razón social de la empresa cliente.
companyNitString?NIT tributario de la empresa.
machineIdStringIdentificador de hardware ligado a la licencia.
planStringNombre comercial del plan contratado (ej: Enterprise Cloud).
maxUsersintLímite máximo de usuarios simultáneos permitidos.
dianDocumentsLimitintCuota de documentos electrónicos autorizados ante la DIAN.
dianDocumentsUsedintCantidad de facturas electrónicas ya emitidas en el ciclo.
issuedAtStringFecha de emisión en formato ISO 8601.
expiresAtStringFecha de expiración en formato ISO 8601.
isTrialboolIndica si la licencia se encuentra en período de prueba gratuito.
statusStringCódigo de estado: ACTIVE, TRIAL, SUSPENDED, REVOKED, EXPIRED.
serverMessageString?Mensaje informativo o motivo de rechazo emitido por el servidor.
rawTokenString?Cadena compacta original del token firmado.

Métodos Auxiliares​

// 1. Verificar si un módulo específico está habilitado
bool canUseAi = license.hasFeature('ai_copilot');

// 2. Extraer metadatos personalizados
String? sucursal = license.getCustom<String>('sucursal');

🛡️ 5. Módulo AntiTamperHelper (Defensa contra Parcheo de Binarios)​

Proporciona contramedidas avanzadas para mitigar la ingeniería inversa y el atraso de reloj:

Métodos del AntiTamperHelper​

  • deriveEncryptionKey(String rawToken, {String salt}):
    • Deriva una clave AES-256 de 32 bytes a partir de la firma Ed25519 del token legítimo.
    • Se utiliza para inicializar SQLCipher o cifrar datos sensibles. Si un atacante parchea la validación booleana (JZ a JNZ), no tendrá la firma matemática y la base de datos no podrá descifrarse.
  • verifyClockIntegrity({...}):
    • Comprueba que el reloj del sistema operativo no haya sido atrasado deliberadamente comparando el timestamp actual contra el último registro y validando el checksum criptográfico.
  • isDebuggerAttached():
    • Detecta si la aplicación se está ejecutando bajo la inspección de un depurador en tiempo de ejecución.

🔒 6. Módulo UiGuard (Protección de Vistas & Widgets en Flutter)​

Permite condicionar el renderizado de pantallas completas o componentes específicos:

// 1. Proteger una pantalla o widget completo
Widget screen = UiGuard.guardView(
license: license,
builder: () => const FacturacionView(), // Solo se construye si la licencia es válida
fallback: () => const PantallaBloqueoWidget(), // Fallback si está suspendida
);

// 2. Proteger un módulo específico por Feature Flag
Widget aiPanel = UiGuard.guardFeature(
license: license,
requiredFeature: 'ai_copilot',
builder: () => const PanelAsistenteIa(),
fallback: () => const SizedBox.shrink(),
);

// 3. Ejecutar cálculos de negocio protegidos
final total = UiGuard.guardAction(
license: license,
onAuthorized: () => calcularRetencionesReales(),
onUnauthorized: () => 0.0,
);

🚀 7. Ejemplo de Integración Completa en Flutter​

import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:valid_license/valid_license.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: '.env');

// 1. Configurar cliente
final client = ValidLicenseClient(
serverUrl: dotenv.env['VALID_LICENSE_SERVER_URL'] ?? 'https://licencias.labtec.sbs',
publicKeyHex: dotenv.env['ED25519_PUBLIC_KEY'],
);

// 2. Obtener token local almacenado
final storedToken = await LocalStorage.getLicenseToken();

// 3. Ejecutar verificación
final ValidLicenseRecord license = await client.verify(
currentToken: storedToken,
companyNit: '900.888.777-1',
companyName: 'Comercializadora Global SAS',
features: ['electronic_billing', 'multi_currency'],
onRevoked: () async {
print('⛔ LICENCIA REVOCADA EN EL VPS. Eliminando credenciales...');
await LocalStorage.clearLicense();
},
);

// 4. Decisión de Flujo de UI
if (license.isActive) {
if (license.rawToken != null && license.rawToken != storedToken) {
await LocalStorage.saveLicenseToken(license.rawToken!);
}
runApp(MainApp(license: license));
} else {
runApp(LicenseLockScreen(license: license));
}
}