Peu d'appels.
N’importe quelle plateforme.
Identity Layer s'intègre avec une surface minimale de par sa conception. MicroPython sur un microcontrôleur, Flutter sur Android et Windows, ou un PHP bridge déposé dans vos points de terminaison existants. Le même protocole. Les mêmes garanties cryptographiques. Quatre appels.
IdentityIoT,
installé via 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})
| Composant | Modèle | Rôle |
|---|---|---|
| MCU | Raspberry Pi Pico 2W (RP2350) | TrustZone, WiFi, stockage des clés device-bound |
| NFC reader | PN532 | Interface I2C, lit les badges NTAG424 DNA |
| Afficher | SSD1306 128x64 OLED | Séquence de démarrage, état de vérification |
| NFC badge | NTAG424 DNA (NXP) | AES-128 intégré, clés non exportables, dynamique CMAC |
Les nœuds Identity IoT ne sont pas limités à l’accès NFC. Le même node authentifié peut transporter n’importe quelle charge utile de capteur. Toutes les lectures sont transmises via AES-256-GCM crypté POST après que le node ait obtenu son JWT.
| Type de capteur | Matériel typique | Champ de charge utile |
|---|---|---|
| Température | DHT22, DS18B20, BME280 | température (flotteur, °C) |
| Humidité | DHT22, BME280 | humidité (flotteur, %) |
| Pression | BME280, BMP390 | pression (flotteur, hPa) |
| Mouvement PIR | HC-SR501 | mouvement (booléen) |
| GPS | NÉO-6M, L76X | lat, lon, altitude (flotteur) |
| Comptage d'énergie | PZEM-004T, INA219 | tension, courant, puissance (float) |
| Accès NFC | PN532 + NTAG424 DNA | badge_uid, accordé (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 lu. Défi lancé.
Ed25519 vérifié.
Identity Tag est la couche d'application construite au-dessus de Identity IoT. Chaque lecture badge déclenche un cycle complet Ed25519 challenge/response contre le backend. Aucun secret stocké sur le reader. Aucune identité en clair sur le badge. Multi-tenant par conception.
# 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
| Mode | Description | Cas d'utilisation |
|---|---|---|
| STD | Badge fonctionne sur n'importe quel terminal de la flotte autorisée pour ce tenant. | Accès au bureau, points d'entrée généraux. |
| HS | Badge lié cryptographiquement à un terminal spécifique. La dérivation DEK intègre un hash de la clé publique du terminal. Badge n'est pas valide sur tout autre reader. | Salles de haute sécurité, racks de datacenter, armoires d'équipement. |
| Point de terminaison | Méthode | Rôle |
|---|---|---|
| identity_tag/it_request_challenge | POST | Émettre un avis occasionnel pour la vérification badge |
| identity_tag/it_verify_badge | POST | Vérifiez la signature Ed25519, renvoyez GRANTED/DENIED |
| identity_tag/it_node_checkin | POST | Node battement de coeur et JWT rafraîchissement |
| identity_tag/it_register_badge | POST (manager) | Écrivez la clé publique et les droits badge |
| identity_tag/it_list_nodes | GET (manager) | Statut de la flotte Node pour tenant |
L'application de provisionnement et de gestion fonctionne sur Android. Il utilise le NFC de l'appareil pour écrire les charges utiles badge et se connecte au backend via des canaux Identity Layer authentifiés.
// 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);
Identité liée à l'appareil.
Quatre appels.
Forfaits locaux. Pas de service d'identité externe. La clé privée ne quitte jamais l'appareil.
Stockage sécurisé via flutter_secure_storage
et TEE / Secure Enclave lorsque disponible.
Le chiffrement de la charge utile s'effectue de bout en bout via 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
| Plate-forme | Statut | Stockage sécurisé |
|---|---|---|
| Androïde | Production, Google Play autorisé | Magasin de clés Android / TEE |
| Fenêtres | Production, Microsoft Store autorisé | Gestionnaire d'informations d'identification Windows |
| Linux | Production | libsecret |
| iOS/MacOS | Sur la feuille de route | Secure Enclave |
5 Ko.
Votre stack, inchangé.
Le PHP bridge est une exigence unique qui se trouve devant vos points de terminaison existants. Vos itinéraires, votre base de données, votre logique métier. Intouché.. Identity Layer gère l'authentification et le cryptage de la charge utile. Supprimez-le de la même manière que vous l'avez ajouté.
require_once
et deux appels de fonction. Vos points de terminaison commencent à recevoir des charges utiles vérifiées et déchiffrées.
Aucune migration de schéma. Aucune modification de la table utilisateur. Pas de stockage de session.
La pile complète côté serveur est 225 Ko.
// 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 point final
enregistre le mandat et ouvre une fenêtre d’export de 72 heures.
Les journaux sont détruits après la collecte. L'autorité requérante assume la responsabilité juridique.
// 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 );
| Composant | Technologie | Remarques |
|---|---|---|
| Durée d'exécution | PHP 8.x | Vanille, aucun cadre requis |
| Base de données | MySQL / MariaDB | Schéma standard, pas de colonnes d'informations d'identification |
| Cache / éphémère | Redis | Jetons de défi, files d'attente de messages (TTL < 1 min) |
| Taille du pont | 5 Ko | Fichier PHP unique, déposez-le dans n'importe quelle pile LAMP |
| Taille de la pile complète | 225 Ko | Identité STD + HS + Clés + Canal sécurisé + IoT + Étiquettes + Tag |
| Déploiement | Conteneur / sur site / air-gapped | Pas de connectivité cloud obligatoire |