Improving firmware development
Building firmware for embedded devices—like microcontrollers and IoT hardware—is hard. It's often complex, it requires deep expertise, and most importantly it is prone to security bugs. One of the key challenges is the limited resources available on these devices, such as constrained processing power, memory, and storage capacity. These constraints make implementing robust security measures at odds with performance and functionality. Unsafe IoT devices are then recruited by cyber criminals into botnets, to perform DDoS attacks, steal information, and act as proxies to evade detection (e.g. the Mirai botnet).
Today, we introduce a new framework that makes it easier to build and maintain safer embedded systems: Wasefire.
Wasefire simplifies the development process and incorporates security best practices by default. This enables developers to create secure firmware without requiring extensive security expertise and only focusing on the business logic they want to implement. To this end, Wasefire provides for each supported device, a platform on which device-agnostic sandboxed applets can run. Wasefire currently supports nRF52840 DK, nRF52840 Dongle, nRF52840 MDK Dongle, and OpenTitan Earlgrey. There is also a Host platform for testing without embedded devices.
The platform is written in Rust for its performance and built-in memory safety. Embedded devices are one of the four target domains of the Rust 2018 roadmap. So today, it is quite simple to write embedded code in Rust, or even integrate Rust in existing embedded code.
The platform expects the applets to be written in—or more realistically, compiled to—WebAssembly for its simplicity, portability, and security. WebAssembly is a binary instruction format for a stack-based virtual machine. It is designed for high-performance applications on the web (hence its name) but it also supports non-web environments. Fun fact: Wasefire uses WebAssembly in both environments: the main usage is non-web for the virtual machine to run applets, but the web interface of the Host platform also relies on WebAssembly.
Incidentally, WebAssembly is another one of the four target domains of the Rust 2018 roadmap. This means that writing applets in Rust and compiling them to WebAssembly is very simple. For this reason, Rust is the primary language to write applets for Wasefire. Starting a new project is as simple as the following steps:
WebAssembly on microcontrollers
Running WebAssembly on microcontrollers may seem like overkill if it were only for sandboxing. But using a virtual machine also provides binary-level portability like Java Cards. In particular, the same WebAssembly applet can be distributed in binary form and run on multiple platforms.
On a microcontroller, every byte matters. To cater to a variety of needs, Wasefire provides multiple alternatives to balance between security, performance, footprint, and portability:
- WebAssembly applets: Platforms may embed the Wasefire interpreter. This is a custom in-place interpreter for WebAssembly in the style of "A fast in-place interpreter for WebAssembly" with a very small footprint. The main drawback is that it doesn't support computation heavy applets.
- Pulley applets: Platforms may embed Wasmtime and its Pulley interpreter. WebAssembly was not designed for interpretation, but for compilation. So WebAssembly interpreters will experience some form of challenge (either performance or footprint). On the contrary, Pulley was designed for fast interpretation and can be compiled from WebAssembly. The main drawback is the larger footprint of this solution and the need for applets to be signed (which is not yet implemented) since Pulley cannot be validated like WebAssembly.
- Native applets: Platforms may link with an applet compiled as a static library for the target architecture. This solution is only provided as a last resort when no other existing alternative works. The main drawback is that almost all security benefits are nullified and binary-level portability is lost.
- CHERI applets: This alternative is planned (but not yet started) and would provide the performance and footprint advantage of Native applets while retaining the sandboxing advantage of WebAssembly and Pulley applets. The main drawback is that the target device needs to support CHERI and binary-level portability is lost.
To illustrate this tradeoff, let's look at a few examples from the Wasefire repository:
- The first example is a button-controlled blinking LED. This applet can run as a WebAssembly applet without problem.
- The second example is a FIDO2 security key implemented using the OpenSK library. This applet reaches the limits of the WebAssembly in-place interpreter in terms of performance at the moment. By using a Pulley applet instead, performance can be improved by degrading applet size and memory footprint.
- The third example is a BLE sniffer. Performance is a critical aspect of this applet. The in-place interpreter is too slow and many packets are dropped. Compiling this applet to Pulley doesn't drop any packet in a noisy BLE environment.
We can summarize the tradeoff in the table below. The platform size differs between examples because the second and third examples need optional drivers disabled by default. The platform is the nRF52840 DK. For the security key, applet performance is measured as the time between a FIDO2 GetInfo request and the last packet of its response. For the BLE sniffer, applet performance is measured as the number of processed packets per second. This metric saturates for Pulley and Native applets, so we only get a lower bound of performance in those cases.
| Blinking LED | WebAssembly | Pulley | Native |
|---|---|---|---|
| Platform size (KiB) | 98 | 299 | 49 |
| Applet size (KiB) | 3.3 | 12 | 5.6 |
| Platform memory (KiB) | 10 | 80 | 5 |
| Security key | WebAssembly | Pulley | Native |
| Platform size (KiB) | 133 | 334 | 80 |
| Applet size (KiB) | 125 | 247 | 73 |
| Platform memory (KiB) | 20 | 104 | 9 |
| Applet performance (ms) | 1191 | 60 | 23 |
| BLE sniffer | WebAssembly | Pulley | Native |
| Platform size (KiB) | 102 | 303 | 53 |
| Applet size (KiB) | 7.2 | 18 | 7.6 |
| Platform memory (KiB) | 16 | 82 | 8.8 |
| Applet performance (packet/s) | = 55 (dropping packets) | > 195 (not dropping) | > 195 (not dropping) |
Looking forward
Wasefire is still an experimental project. Many features are missing (including security features) and many improvements are planned. For example, the platform currently runs a single applet and provides all the resources this applet asks for. Ultimately, applets would come with a manifest describing which resources they are permitted to use, and those resources would be isolated to that single applet. It would also be possible to run multiple applets concurrently.
The project is open source, so bug reports, feature requests, and pull requests are welcome. The project is licensed under Apache-2.0, so commercial use is permitted.
Feel free to give it a try (no hardware needed) and share the word!


