13.2 Device Identity and Trusted Data
13.2.1 A DID-Based Device Identity Design
The basic shape of traditional IoT identity management: devices are flashed with a symmetric key or an X.509 certificate at the factory, and validated by a centralized authentication server when they connect to a platform. This model works well within a single platform, but the moment a device needs to exchange data across organizations — say, an in-vehicle temperature-humidity sensor reporting simultaneously to a logistics system and a traffic management system — that centralized registry becomes a bottleneck and a single point of failure. A device that wants to switch platforms must have its identity re-flashed; and data receivers cannot independently verify the device itself — they can only trust the platform that issued the certificate.
The Decentralized Identifier (DID) offers a different path. The design idea behind DIDs is that the identifier is not managed by a single registry; instead, it is generated and controlled autonomously by the identifier's subject — the device itself or its legitimate controller. A DID's string structure takes the form did:<method-name>:<method-specific-id>, for example did:example:abcd1234, where example is the DID method and abcd1234 is the unique identifier within that method's namespace. A DID's core value lies not in the string itself but in the DID document obtained by resolving it. This structured data (usually in JSON-LD format) contains the currently valid public-key list, service endpoints, and authentication protocols. It answers the verifier's question: "which public key should I use to check the signature of the party claiming to be this device?" (The W3C DID Core standard defines the DID core data model and operational semantics.)
Deploying this scheme on IoT devices requires solving three engineering problems one by one: physically binding keys to hardware, publishing and updating the DID document in the selected verifiable data registry, and deactivation and recovery after a device is lost or its private key leaks. The registry can be a distributed ledger, decentralized file system, database, or other trusted storage. The exact mechanism is defined by the DID Method; DID Core does not require a blockchain.
Binding keys to the device: physical anchoring. In high-assurance scenarios, private keys should be generated and kept non-exportable in a secure element, Secure Enclave, or TPM whenever possible, with the main controller invoking only the signing interface. The actual protection level still depends on device certification, the supply chain, and resistance to side-channel attacks. How a DID's method-specific-id is constructed is defined by the DID Method and is not universally a public-key hash. If a method chooses content addressing, it can prescribe a specific digest algorithm. SHA-256 and Ethereum's common Keccak-256 can both produce fingerprints, but their outputs differ and are not interchangeable. The verifier must use exactly the algorithm and canonical byte sequence agreed at registration.
In engineering trade-offs, choosing a secure enclave means balancing cost against protection level. High-volume consumer devices (such as smart light bulbs) are cost-sensitive: the private key may live inside a secure chip that cannot withstand side-channel attacks. Industrial-grade devices (such as medical infusion pumps) require dedicated secure elements with higher certification levels. This is identity management's fundamental engineering judgment: an acceptable balance must be found between protection level and deployment cost.
Registration and update: defined by the DID Method. After generating a DID, the controller publishes the information needed for resolution to a verifiable data registry according to the chosen DID Method. A permissioned-ledger method may store the DID document hash and control key in a smart contract, while methods based on the Web, databases, or peer-to-peer registries have different creation, update, and deactivation processes. The key requirement is not that the data "must be on chain," but that a resolver can verify that the current controller authorized the update. The Solidity code below demonstrates only one educational on-chain registry implementation; it is not the universal DID Core workflow:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract DeviceDIDRegistry {
struct DIDDocument {
address owner; // on-chain address controlling this DID
bytes32 publicKeyHash; // public key hash (the key itself would also work; a hash keeps storage small)
uint256 timestamp; // registration or last-update timestamp
bool isActive; // whether the DID is active
}
mapping(bytes32 => DIDDocument) private didDocs; // DID hash -> document
event DIDRegistered(bytes32 indexed didHash, address indexed owner, uint256 timestamp);
event DIDUpdated(bytes32 indexed didHash, address indexed owner, uint256 timestamp);
event DIDRevoked(bytes32 indexed didHash, uint256 timestamp);
// register: register a device by its DID and public key hash
function registerDevice(string calldata _did, bytes32 _publicKeyHash) external {
bytes32 didHash = keccak256(bytes(_did));
require(didDocs[didHash].timestamp == 0, "DID already registered");
didDocs[didHash] = DIDDocument({
owner: msg.sender,
publicKeyHash: _publicKeyHash,
timestamp: block.timestamp,
isActive: true
});
emit DIDRegistered(didHash, msg.sender, block.timestamp);
}
// verify: given a DID and message hash, check whether the address recovered by ecrecover matches the controller
// (illustrative implementation: the comparison of publicKeyHash with the signing key is omitted; production deployments should add it)
function verifySignature(
string calldata _did, bytes32 _messageHash,
uint8 _v, bytes32 _r, bytes32 _s
) external view returns (bool) {
bytes32 didHash = keccak256(bytes(_did));
DIDDocument storage doc = didDocs[didHash];
require(doc.isActive, "Device is not active");
address signer = ecrecover(_messageHash, _v, _r, _s);
return (signer == doc.owner);
}
// revoke: only the device owner may call this
function revokeDevice(string calldata _did) external {
bytes32 didHash = keccak256(bytes(_did));
require(didDocs[didHash].owner == msg.sender, "Not the owner");
didDocs[didHash].isActive = false;
emit DIDRevoked(didHash, block.timestamp);
}
}This contract captures the minimal operation set of identity management. Each registration records an on-chain address as the owner; verifySignature uses Solidity's ecrecover to recover the signing address from the signature and compare it with the owner. This implies an engineering premise: the device must be able to construct a valid Ethereum-format transaction, or produce an offline signature that ecrecover can verify. In actual deployments, the full DID document (with public keys and service endpoints) is usually hosted on off-chain storage such as IPFS, while the chain keeps only the IPFS hash and a document pointer, to reduce storage cost.
The corresponding key engineering checkpoints:
- Key generation: ensure that keys generated in the secure enclave cannot be exported by the main MCU.
- DID construction: confirm that the hash algorithm for the
method-specific-idmatches the contract'skeccak256(the Ethereum standard). - Transaction signing: verify that the format of the device's offline-signed transactions can be correctly parsed by
ecrecover. - On-chain state: check that the
isActivefield remainstrueafter registration and updates. - Off-chain storage: confirm that the DID document's IPFS hash matches the on-chain pointer.
Deactivation and recovery: propagation delay still exists. In an on-chain method, the controller can call revokeDevice and mark the state inactive after transaction confirmation. Other DID Methods may deactivate an identifier by updating their registry or resolution metadata. Every approach has visibility delays caused by submission, replication, caching, and offline verification; none can promise "instant network-wide revocation." Verifiers should define a maximum cache lifetime for resolution results, a signature time window, and online status checks for high-risk actions. If a device loses its private key, a recovery key or multi-party recovery strategy must already be in place.
In addition, if a device is physically destroyed, the private key has not leaked, but the device can no longer initiate signed transactions. This calls for a pre-configured "successor" or "recovery key." The typical approach is to designate a backup public-key address at registration (for example, a factory administration key); that key is entitled to execute revocation after presenting "proof of device death" (for example, no heartbeat for N consecutive periods). This design adds complexity to the on-chain logic and is usually omitted from minimal contracts, but it is worth including in a real product.
The core shift introduced by DID is to make identifier control, resolution, and key-rotation rules explicit in a DID Method; it does not inherently move trust to a blockchain. An on-chain method depends on ledger consensus and bears transaction, synchronization, and governance costs. A non-chain method depends on the trust assumptions of its registry, domain name, database, or peer-to-peer network. In either case, device hardware proves only that a key was used in a protected environment. It cannot by itself prove that a sensor reading is true or replace the governance responsibilities of manufacturing, calibration, and operations organizations.
The sequence diagram below shows the complete identity lifecycle from factory provisioning through registration to verification.
13.2.2 The On-Chain Data Model: Off-Chain Storage and On-Chain Fingerprints
Having settled "who is this device", the next question to answer is: "how can anyone believe that the data this device produces is genuine and complete?" A temperature sensor reporting one reading per second produces 86,400 records a day. Writing all of them to the blockchain would inflate costs beyond acceptance — mainstream blockchains' block space and network throughput simply cannot absorb a millisecond-scale torrent of data from massive numbers of devices. Dumping raw data onto the chain wholesale is neither economical nor necessary.
A common approach is to store raw data off chain and record a hash commitment in a ledger. The ledger stores a digest for comparison rather than the complete content. Its role is to make post-submission rewriting more detectable, not to make off-chain data inherently trustworthy or absolutely immutable.
The hash function is the cornerstone of the whole model. It compresses data of any size (a photograph, a 1 KB temperature curve) into a fixed-length digital fingerprint, usually 256 bits. The same data always yields the same hash; change even a single bit, and the hash changes completely. With this property, the blockchain needs to store only the hash — anyone who later obtains the raw data can run the hash computation and verify whether it has been altered.
In actual engineering, putting a data record on chain takes roughly five steps (see Figure 13-4).
Sensor sampling: the temperature sensor reads 25.3 °C and produces a JSON record
{"device_id":"sensor001","temp":25.3,"ts":1700000000}.Edge-node aggregation and hashing: the edge gateway or fog node receives data from multiple devices, packs it into a data block, and computes the block's hash. When large numbers of devices are involved, a Merkle tree can also be built — hashes of multiple records are concatenated pairwise and hashed again, ultimately producing the root hash. The Merkle tree's engineering value: to verify that a particular record was in the original package, you need not download the entire package, only the path of hashes from that record to the root, and the path size grows logarithmically with the number of nodes. That is of real practical significance where IoT devices have limited bandwidth.
Off-chain storage: raw data may enter object storage, a controlled database, or a content-addressed network. An IPFS content identifier corresponds to specific bytes, but the content may still become unavailable when nobody pins it, nodes go offline, or access policy intervenes. Arweave aims at long-term persistence, but its payment, gateway, and availability assumptions still require assessment. An off-chain design must define replication, retention, encryption, deletion, and forensic responsibility rather than merely calling the storage "decentralized."
Ledger submission: the edge node submits the data-block hash, Merkle root, and necessary metadata as a transaction, and the contract records them as an event or state. Block time indicates approximately when the network accepted the transaction; it is neither capture time nor, by itself, a legal guarantee of non-repudiation. Preserve the device signature, trusted time source, submitter identity, and finality evidence as well.
Verification: the verifier retrieves the off-chain data, recomputes the hash with the agreed canonicalization and algorithm, and compares it with the ledger record. A match means only that the current bytes match the committed digest, not that the content is true. A mismatch means that at least one of the copy, encoding, chunking, or digest differs and requires investigation; it does not by itself prove malicious tampering.
The overall flow assigns the most expensive parts — "mass data storage" and "high-frequency writes" — to the off-chain side, leaving the blockchain only the most compact "evidentiary fingerprint." This design directly addresses the two central concerns of IoT deployment at scale: cost and trustworthiness.
The model's boundary is that a hash compares byte consistency only. If the sensor, gateway, or canonicalization process was already wrong before the digest was formed, the ledger will faithfully record the wrong digest. Multi-party signatures, calibration, spot checks, and anomaly detection can reduce the risk, but they cannot eliminate source fraud or collusion.
Engineering checklist
- Hash algorithm choice: for general off-chain scenarios SHA-256 remains the default; when entering Ethereum-family contracts, use keccak256 uniformly (consistent with the illustration in 13.2.1), and keep the on-chain/off-chain convention consistent. BLAKE2 or SHA-3 can serve as alternatives, but confirm whether the smart-contract virtual machine supports them natively.
- Off-chain storage choice: IPFS suits data-sharing scenarios with moderate access frequency; Arweave's pay-once permanent-storage model suits regulatory compliance needs. Neither should impose a hard dependency on end devices.
- Timestamp alignment: strictly speaking, block confirmation time is the "on-chain time." Device local time can serve only as a reference during verification and should not be the sole evidentiary anchor.
13.2.3 Data Verification and Traceability Mechanisms
Putting hashes on chain solves the verification problem of "whether the data has been tampered with." You take a piece of data, compute its hash, compare it with the hash stored on chain, and if they match, you conclude the data is untouched. But that only answers "was it altered after going on chain"; the deeper question is: was the data itself trustworthy at the moment it went on chain? A record is born at the sensor, passes through edge-node aggregation and forwarding, and is finally written to the blockchain — how many hops in between, and what processing occurred at each hop. If these steps go unrecorded, any claim of "traceability" is an empty promise.
Data verification and traceability mechanisms must cover two levels. The first is integrity verification: the smart contract exposes a public verification function; anyone submits the raw data and its data ID, and the contract recomputes the hash and returns "valid/invalid" by comparison. The second is provenance tracking: every data report, forwarding, and verification leaves a set of event logs on chain, recording who did what to which data record, and when. Strung together, these logs form an auditable chain of data flow.
Start with integrity verification. The Solidity contract below demonstrates the core logic: compute the raw data's hash with keccak256 and compare it against the fingerprint stored on chain in advance.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract DataVerification {
mapping(bytes32 => bytes32) private dataHashes; // data ID -> hash
mapping(bytes32 => uint256) private dataTimestamps; // data ID -> on-chain timestamp
mapping(bytes32 => address) private dataOwners; // data ID -> device address
// event: record a data fingerprint going on chain
event DataStored(
bytes32 indexed dataId,
bytes32 dataHash,
uint256 timestamp,
address indexed device
);
// event: record the verification result
event DataVerified(
bytes32 indexed dataId,
bytes32 actualHash,
bool isValid,
address indexed verifier
);
// store a data fingerprint
function storeDataHash(bytes32 dataId, bytes32 dataHash) external {
require(dataHashes[dataId] == bytes32(0), "Data ID already exists");
dataHashes[dataId] = dataHash;
dataTimestamps[dataId] = block.timestamp;
dataOwners[dataId] = msg.sender;
emit DataStored(dataId, dataHash, block.timestamp, msg.sender);
}
// verify integrity of raw data
function verifyData(bytes32 dataId, bytes memory rawData) external returns (bool) {
bytes32 storedHash = dataHashes[dataId];
require(storedHash != bytes32(0), "Data ID not found");
bytes32 computedHash = keccak256(rawData);
bool isValid = (computedHash == storedHash);
emit DataVerified(dataId, computedHash, isValid, msg.sender);
return isValid;
}
}There are only two core functions. storeDataHash handles going on chain: it writes the data ID and hash fingerprint into the contract's mapping, while recording the timestamp and device address. verifyData handles verification: the verifier passes in the data ID and raw data, the contract computes the hash automatically, and the comparison result is broadcast through the DataVerified event. Any auditor, consumer, or regulator can listen to this event to confirm whether the data is genuine.
Event logs (Events) are a very low-cost data-recording mechanism in Solidity. Writing an event costs far less gas than modifying a storage variable. Each event can carry up to three indexed parameters; blockchain clients index these parameters, and off-chain programs can use the indexes to filter relevant records quickly. In this contract, dataId and device are marked indexed, which means that knowing either the data ID or the device address is enough to locate all related events quickly through a block explorer or Web3 tooling. In high-frequency data scenarios, this is far more efficient than traversing the entire chain.
Integrity verification combined with event logs forms the first layer of traceability: one record, one hash, one verification, one event. But sometimes a record passes through multiple nodes: an edge gateway first aggregates a batch of sensor readings and forwards them to a plant server, and only after format validation does the plant server submit them on chain. Every stage should leave a record on chain. At this point, the traceability mechanism must string multiple events into one complete flow.
Cross-domain traceability is achieved through the "data ID." The raw data a device produces keeps one globally unique data ID throughout its entire lifecycle — typically generated by hashing the sensor ID and timestamp together. After each processing node completes its operation, it calls the contract to record an event whose parameters include the "previous handler's address." By walking through all events associated with a data ID, an off-chain traceability application can reconstruct the complete data-flow path.
In engineering terms, this mechanism faces two constraints.
The first constraint is the storage boundary of event logs. Ethereum's per-block gas limit caps the total number of events a block can hold. Recording high-frequency IoT data on chain one record at a time is not feasible; aggregation must happen at the edge node first. The common practice is to put the Merkle root of a batch of data on chain every 5 or 10 minutes, and to provide a Merkle proof when verifying an individual data record.
The second constraint is cross-chain flow. If data flows across multiple independent blockchain networks (say, a production chain, a logistics chain, and a consumption chain), the data ID must be unified across chains. Cross-chain bridges must map the data ID and its events onto the target chain so that traceability queries are never interrupted. The concrete engineering details of such schemes are discussed in Section 13.4.3.
Seen from the contract side, data verification and traceability consist of digest comparison plus event records. The digest can show whether the current copy matches the committed value, and an event can show that an identity submitted an operation under the ledger's rules. Whether this becomes non-repudiable evidence still depends on signing keys, time, finality, off-chain originals, and legal rules. It also cannot answer whether a device is qualified to make a claim — for example, whether its calibration certificate remains valid or who issued a quality-inspection conclusion. The next subsection's verifiable credentials address that class of statement.
13.2.4 Verifiable Credentials (VC) and Trusted Device Claims
DIDs answer "who this device is and which public key verifies it," but the more frequent question in industrial collaboration is "whether this device is qualified to do something": whether a sensor was calibrated within its validity period, whether a pressure vessel passed factory quality inspection, whether an electricity meter holds network-access certification. These claims need to be portable, verifiable offline, and independent of the issuing body being on call. The Verifiable Credential (VC) is the standard carrier designed for exactly this.
VCs follow the issuer — holder — verifier triangle model. Take device calibration as an example: once the metrology institute completes a calibration, it constructs a structured claim (device DID, calibration date, validity period, error bounds), signs it with the institute's private key, and hands it to the device or its gateway — the holder stores the credential in local secure storage; from then on, whether it is a purchaser, a regulatory platform, or a cross-domain collaboration system, whoever obtains the credential can verify its authenticity offline with the issuer's public key, with no call-back to the metrology institute. Quality-inspection credentials work the same way: the factory inspection report is issued as a VC and travels with the batch, and the downstream whole-plant acceptance checks each one in turn, instead of pulling archives and mailing inquiries.
The division of labor between VCs and DIDs must be kept straight. A DID document answers "who controls this identifier and which verification methods apply," and is resolved through the registry associated with the selected DID Method. A VC carries "who made which verifiable claim about what subject" and is presented by the holder as needed. VCs do not require DIDs, and a DID does not automatically confer any business qualification on a device. On standardization, W3C VC Data Model 2.0 became a Recommendation in May 2025. This book uses DID Core v1.0, published as a W3C Recommendation in July 2022. An engineering implementation should pin a specific specification version and DID Method rather than substituting the status of an evolving editor's draft for a published standard.
With identity and verifiable claims both in place, the next step is to put these capabilities into the first complete cross-organizational scenario — supply-chain traceability.