Pocas llamadas.
Cualquier plataforma.
Identity Layer se integra con una superficie mínima por diseño. MicroPython en un microcontrolador, Flutter en Android y Windows, o un PHP bridge colocado en sus puntos finales existentes. El mismo protocolo. Las mismas garantías criptográficas. Cuatro llamadas.
IdentityIoT,
instalado a través de mip.
# Install via mip — no external dependencies # import mip; mip.install("github:wide/identity-micropython") from identity_iot import IdentityIoT # 1. Init — configure node identity identity = IdentityIoT( base_url = cfg['base_url'], node_type = cfg['node_type'], tenant_id = cfg['tenant_id'], comm_key = cfg['comm_key'].encode(), comm_iv = cfg['comm_iv'].encode(), ) # 2. Register — device-bound key, runs once identity.ensure_registered() # 3. Authenticate — Ed25519 challenge / verify / JWT token = identity.authenticate() # 4. Authenticated request — AES-256-GCM encrypted payload response = identity.post_encrypted({'request': 'record_sensor_data', 'value': 42})
| Componente | Modelo | Role |
|---|---|---|
| MCU | Raspberry Pi Pico 2W (RP2350) | TrustZone, WiFi, almacenamiento de claves device-bound |
| NFC reader | PN532 | Interfaz I2C, lee insignias NTAG424 DNA |
| Mostrar | SSD1306 128x64 OLED | Secuencia de inicio, estado de verificación |
| NFC badge | NTAG424 DNA (NXP) | AES-128 integrado, claves no exportables, dinámicas CMAC |
Los nodos Identity IoT no están limitados al acceso NFC. El mismo node autenticado puede transportar cualquier carga útil del sensor. Todas las lecturas se transmiten a través de AES-256-GCM POST cifrado después de que el node obtiene su JWT.
| Tipo de sensor | Hardware típico | Campo de carga útil |
|---|---|---|
| Temperatura | DHT22, DS18B20, BME280 | temperatura (flotación, °C) |
| Humedad | DHT22, BME280 | humedad (flotación,%) |
| Presión | BME280, BMP390 | presión (flotación, hPa) |
| movimiento PIR | HC-SR501 | movimiento (booleano) |
| GPS | NEO-6M, L76X | lat, lon, altitud (flotación) |
| Medición de energía | PZEM-004T, INA219 | voltaje, corriente, potencia (flotación) |
| NFC acceso | PN532 + NTAG424 DNA | Badge_uid, concedido (bool) |
{ "node_uid" : "273ec83a-84c8-4967-aafa-0780ab161cc1", "tenant_id" : "0b3cfe4d-fdf7-46c2-bd41-b533da415dd9", "node_type" : "sensor", "temperature": 24.3, "humidity" : 58.1, "pressure" : 1013.2, "updated_at" : "2026-06-09 11:42:07" }
Badge leer. Reto emitido.
Ed25519 verificado.
Identity Tag es la capa de aplicación construida sobre Identity IoT. Cada lectura de badge desencadena un ciclo Ed25519 challenge/response completo contra el backend. No hay secretos almacenados en el reader. No hay identidad de texto claro en el badge. Multi-tenant por diseño.
# 1. Reader reads NTAG424 DNA NDEF payload # Fields: epk (encrypted private key), df2 (DEK fragment 2), mode (STD|HS) # 2. DEK reconstruction dek = df1_from_config XOR df2_from_badge # 3. Decrypt badge private key badge_privkey = AES_CBC_decrypt(dek, epk) # 4. Request challenge from backend challenge = POST identity_tag/it_request_challenge # { node_uid, badge_uid, tenant_id } # 5. Sign challenge with badge key (Ed25519) signature = ed25519_sign(badge_privkey, challenge_nonce) # 6. Verify at backend result = POST identity_tag/it_verify_badge # { challenge_id, signature_b64 } -> { granted: true } # 7. Reader actuates relay (GRANTED) or displays DENIED
| Modo | Descripción | Caso de uso |
|---|---|---|
| STD | Badge trabaja en cualquier terminal de la flota autorizada para ese tenant. | Acceso a oficinas, puntos de entrada generales. |
| HS | Badge vinculado criptográficamente a un terminal específico. La derivación DEK incorpora un hash de la clave pública del terminal. Badge no es válido en ningún otro reader. | Salas de alta seguridad, racks de centros de datos, gabinetes de equipos. |
| Punto final | Método | Role |
|---|---|---|
| identity_tag/it_request_challenge | POST | Emitir nonce para la verificación badge |
| identity_tag/it_verify_badge | POST | Verifique la firma Ed25519, devuelva GRANTED/DENIED |
| identity_tag/it_node_checkin | POST | Node latido y JWT actualización |
| identity_tag/it_register_badge | POST (manager) | Escriba badge clave pública y derecho |
| identity_tag/it_list_nodes | GET (manager) | Node estado de la flota para tenant |
La aplicación de aprovisionamiento y gestión se ejecuta en Android. Utiliza el NFC del dispositivo para escribir cargas útiles badge. y se conecta al backend a través de canales Identity Layer autenticados.
// identity_tag package - badge provisioning final writer = IdentityTagWriter(api: tagApi); // Generate badge keypair and encrypt private key with split DEK final payload = await writer.prepareBadgePayload( tenantId : tenant.id, mode : BadgeMode.STD, // or BadgeMode.HS nodeUid : null, // required for HS mode ); // Write NDEF to NTAG424 DNA via flutter_nfc_kit await writer.writeToTag(payload);
Identidad vinculada al dispositivo.
Cuatro llamadas.
Paquetes locales. Sin servicio de identidad externo. La clave privada nunca sale del dispositivo.
Almacenamiento seguro mediante flutter_secure_storage
y TEE / Secure Enclave cuando esté disponible.
El cifrado de carga útil es de extremo a extremo a través de ITEMSEncrypter.
// 1. Init — JWT identity, silent on subsequent boots final auth = IdentityAuthServiceStd(ITEMSGlobals.authURI); await auth.ensureJwt( preferredLocale: locale.languageCode, utcOffsetMin: DateTime.now().timeZoneOffset.inMinutes, ); // 2. Handshake — silent if identity already present on device final result = await api.mjHandshake( identityHash: identityHash, publicKey: publicKey, ); // 3. Store JWT — session active, no password, no credential database await _storage.write(key: 'token', value: result['token']); // 4. Every request — AES-256-GCM payload, end-to-end encrypted final response = await api.postEncrypted({'request': 'your_request'});
dependencies: # Identity Layer — local packages, no external registry identity_package: path: packages/identity_package identity_std: path: packages/identity_std identity_hs: path: packages/identity_hs items_crypto: path: packages/items_crypto
| Plataforma | Estado | Almacenamiento seguro |
|---|---|---|
| Androide | Producción, Google Play autorizada | Almacén de claves de Android / TEE |
| ventanas | Producción, Microsoft Store autorizada | Administrador de credenciales de Windows |
| linux | Producción | libresecreto |
| iOS/macOS | En la hoja de ruta | Secure Enclave |
5 KB.
Tu pila, sin cambios.
El PHP bridge es un requisito único que se encuentra frente a sus puntos finales existentes. Tus rutas, tu base de datos, tu lógica de negocio. Intacto.. Identity Layer maneja la autenticación y el cifrado de la carga útil. Elimínelo de la misma manera que lo agregó.
require_once
y dos llamadas a funciones. Sus puntos finales comienzan a recibir cargas útiles verificadas y descifradas.
Sin migración de esquema. No hay cambios en la tabla de usuarios. Sin almacenamiento de sesión.
La pila completa del lado del servidor es 225KB.
// 1. Include the Identity Layer bridge — 5 KB require_once 'identity/_mj_auth.php'; // 2. Authenticate — JWT verified, identity_hash extracted // identity_hash is a 64-char SHA-256 hex — never a user ID [$identityHash, $tenantId, $claims, $role] = mj_authenticate_identity($encoder); // 3. Every response — AES-256-GCM encrypted, end-to-end sendEncryptedResponse($encoder, [ 'status' => 'ok', 'data' => $yourData, ], ['ts' => time()], 200);
mj_mandate_log punto final
registra el mandato y abre una ventana de exportación de 72 horas.
Los registros se destruyen después de su recolección. La autoridad requirente asume la responsabilidad legal.
// Requires role = admin — never operator [$userId, $tenantId, $claims, $role] = mj_authenticate($encoder); mj_require_role($encoder, $claims, 'admin'); // Register mandate — opens 72h export window // target_identity_hash: 64-char SHA-256, never plaintext identity // Ciphertext is never stored — metadata only $connector->executeDatabaseParameterQuery( "INSERT INTO audit_log (mandate_number, issuing_country, issuing_court, target_identity_hash, export_status) VALUES (?, ?, ?, ?, 'PENDING')", [$mandateNumber, $issuingCountry, $issuingCourt, $targetHash], $db );
| Componente | Tecnología | Notas |
|---|---|---|
| Tiempo de ejecución | PHP 8.x | Vainilla, no requiere marco. |
| Base de datos | MySQL / MaríaDB | Esquema estándar, sin columnas de credenciales |
| Caché / efímero | Redis | Tokens de desafío, colas de mensajes (TTL < 1 min) |
| Tamaño del puente | 5 KB | Archivo único PHP, colocarlo en cualquier pila LAMP |
| Tamaño de pila completo | 225KB | Identidad STD + HS + Claves + Canal Seguro + IoT + Etiquetas + Tag |
| Despliegue | Contenedor/local/air-gapped | Sin conectividad a la nube obligatoria |